Foundry 的好处
Foundry 项目使用 solidity 编写测试脚本,除solidity之外,不需要其他语言基础。
主要包含了 4 个工具:forge
, cast
, anvil
, chisel
,这些工具可以通过执行 foundryup 命令安装。
简要介绍几个工具的功能:
forge
: 对项目进行 编译,部署,测试,验证等cast
: 在命令行轻松进行以太坊 RPC 调用,拉取链上信息、调用智能合约、发送交易等anvil
: 在本地创建测试节点,也用于 fork 其他 EVM 兼容的网络chisel
: 在本地开启 solidity 环境,使你能够在命令行编写 solidity 代码并运行。方便快速运行一段 solidity 代码
安装: https://book.getfoundry.sh/getting-started/installation
Forge 测试
https://learnblockchain.cn/docs/foundry/i18n/zh/reference/config/testing.html
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import {Test, console} from "forge-std/Test.sol";
import {Counter} from "../src/Counter.sol";
contract CounterTest is Test {
Counter public counter;
function setUp() public {
counter = new Counter();
counter.setNumber(0);
}
function test_Increment() public {
counter.increment();
assertEq(counter.number(), 1);
}
function testFuzz_SetNumber(uint256 x) public {
counter.setNumber(x);
assertEq(counter.number(), x);
}
}
- setUp:在每个测试用例运行之前调用的可选函数
- test:以 test 为前缀的函数作为测试用例运行
- 作弊码(Cheatcodes):
- prank
- 您可以通过 Forge 标准库的 Test 合约中提供的 vm 实例轻松访问作弊码。
- https://learnblockchain.cn/docs/foundry/i18n/zh/forge/forge-std.html
- 为了将来的确定性,让我们确保我们 revert 了,可以使用expectRevert作弊码来验证我们不是所有者的情况:
import "forge-std/Test.sol"; vm.expectRevert(Unauthorized.selector); vm.prank(address(0)); vm.startPrank(alice);
- expectEmit:
vm.expectEmit(true, true, false, true);
vm.expectRevert(stdError.arithmeticError);
- prank
Forge Std
相关介绍
- https://learnblockchain.cn/docs/foundry/i18n/zh/forge/forge-std.html
- https://learnblockchain.cn/docs/foundry/i18n/zh/reference/forge-std/
STD小结
- Vm.sol:最新的作弊码接口
- console.sol 和 console2.sol:Hardhat 风格的日志记录功能
- Script.sol:Solidity 脚本 的基本实用程序
- Test.sol:DSTest 的超集,包含标准库、作弊码实例 (vm) 和 Hardhat 控制台
导入 Test.sol 并在测试合约中继承 Test :
import {Test, console} from "forge-std/Test.sol";
import "forge-std/Test.sol";
import "forge-std/Test.sol";
contract ContractTest is Test {
...
// Access Hevm via the `vm` instance
vm.startPrank(alice);
// Assert and log using Dappsys Test
assertEq(dai.balanceOf(alice), 10000e18);
// Log with the Hardhat `console` (`console2`)
console.log(alice.balance);
// Use anything from the Forge Std std-libraries
deal(address(dai), alice, 10000e18);
...
}
Traces
Forge 可以为失败的测试(-vvv)或所有测试(-vvvv)生成跟踪(Traces)。
每个跟踪可以有更多的子跟踪(subtraces),每个 subtraces 表示对合约的调用和返回值。
- 绿色:对于不会 revert 的调用
- 红色:用于有 revert 的调用
- 蓝色:用于调用作弊码
- 青色:用于触发日志
- 黄色:用于合约部署
分叉测试
Forge 支持使用两种不同的方法在分叉环境中进行测试:
- 分叉模式(Forking Mode) — 通过forge test –fork-url 标志使用一个单独分叉(例如分叉的以太坊主网)进行所有测试
- 分叉作弊码(Forking Cheatcodes) — 通过 forking 作弊码 在 Solidity 测试代码中直接创建、选择和管理多个分叉
模糊测试
https://learnblockchain.cn/docs/foundry/i18n/zh/forge/fuzz-testing.html
contract SafeTest is Test {
// ...
function testWithdraw(uint96 amount) public {
vm.assume(amount > 0.1 ether);
payable(address(safe)).transfer(amount);
uint256 preBalance = address(this).balance;
safe.withdraw();
uint256 postBalance = address(this).balance;
assertEq(preBalance + amount, postBalance);
}
}
- “μ”(希腊字母 mu)是所有模糊运行中使用的平均 Gas
- “~”(波浪号)是所有模糊运行中使用的中值 Gas
不变性测试
https://learnblockchain.cn/docs/foundry/i18n/zh/reference/config/inline-test-config.html
通过在函数名前加上 test
前缀进行标准测试,通过在函数名前加上 invariant
前缀来表示不变性测试(例如,function invariant_A())。
在目标冲突的情况下,不变性模糊器的优先级为:目标选择器 | 目标工件选择器 > 排除合约 | 排除工件 > 目标合约 | 目标工件
差异测试
https://learnblockchain.cn/docs/foundry/i18n/zh/forge/differential-ffi-testing.html
合约验证
https://learnblockchain.cn/docs/foundry/i18n/zh/forge/deploying.html
Cast 端对端
https://learnblockchain.cn/docs/foundry/i18n/zh/cast/index.html
- 检索 DAI 代币的总供应量
cast call 0x6b175474e89094c44da98b954eedeac495271d0f "totalSupply()(uint256)" --rpc-url https://eth-mainnet.alchemyapi.io/v2/Lc7oIGYeL_QvInzI0Wiu_pOZZDEKBrdf 8603853182003814300330472690
- 解码 calldata
cast 4byte-decode 0x1F1F897F676d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003e7