第3节:forge test
小白入门:https://github.com/dukedaily/solidity-expert ,欢迎star转发,文末加V入群。
职场进阶: https://dukeweb3.com
本节覆盖 forge test 的核心用法:assertion、setUp、常用 cheatcode 与异常断言。
Assertion 系列
forge-std 提供丰富的断言函数:
assertEq(uint256(1), 1); // 相等
assertEq(addr1, addr2, "address mismatch"); // 带错误信息
assertGt(a, b); // a > b
assertLt(a, b); // a < b
assertTrue(x);
assertApproxEqAbs(a, b, 100); // 绝对误差内相等
assertApproxEqRel(a, b, 0.01e18); // 相对误差 1% 内相等
setUp 与状态隔离
每个 test_ 函数运行前都会先执行 setUp(),函数间状态完全隔离:
contract MyTest is Test {
Vault vault;
address alice = makeAddr("alice");
function setUp() public {
vault = new Vault();
deal(address(vault), 100 ether); // 给 vault 注资
}
function test_A() public {
/* 不影响 test_B 的 vault 状态 */
}
function test_B() public {
/* setUp 会重新跑一次 */
}
}
makeAddr("alice") 按字符串生成确定性地址并自动打标签,便于 trace 输出可读。
核心 Cheatcode
Cheatcode 通过 forge-std 暴露的 vm 对象调用:
// 伪装调用者
vm.prank(alice); // 仅下一次调用
vm.startPrank(alice); // 持续直到 stopPrank
vm.stopPrank();
// 操纵区块
vm.warp(block.timestamp + 1 days); // 推进时间戳
vm.roll(block.number + 100); // 推进区块号
// 操纵余额与存储
deal(address(token), user, 1_000 ether); // 直接给地址分发 token
vm.store(target, slot, value); // 直接写 storage slot
// 快照与回滚
uint256 snap = vm.snapshotState();
// ... 做一些操作
vm.revertToState(snap);
Revert 断言
// 期望下一条调用回滚
vm.expectRevert();
vault.withdraw(tooMuch);
// 期望带指定 message
vm.expectRevert("Insufficient balance");
vault.withdraw(tooMuch);
// 期望带 custom error selector
vm.expectRevert(Vault.InsufficientBalance.selector);
vault.withdraw(tooMuch);
Event 断言
vm.expectEmit(true, true, false, true); // topic1, topic2, topic3, data 是否校验
emit Vault.Deposit(alice, 1 ether); // 预期的事件
vault.deposit{value: 1 ether}(); // 触发事件的调用
日志输出
import {console} from "forge-std/Test.sol";
console.log("balance:", vault.balanceOf(alice));
需要用 -vv 或更高 verbosity 才会显示。
小结
掌握 setUp + prank + warp + expectRevert 就能写出大部分单测。下一节把视野扩展到主网——Fork 测试。