第六天:合约测试与调试——让代码先证明自己
合约一旦部署,改代码 = 重新部署整套(状态迁移),bug 的代价远高于普通软件。所以”先写测试”在合约开发里不是美德,是生存技能。今天给第五天的投票合约补上完整的测试。
用 Solidity 写测试
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import "forge-std/Test.sol";
import {Votes} from "../src/Votes.sol";
contract VotesTest is Test {
Votes public votes;
function setUp() public {
votes = new Votes();
votes.addCandidate("Alice");
votes.addCandidate("Bob");
}
function test_VoteCountIncreases() public {
votes.vote(1);
(, uint256 count) = votes.candidates(1);
assertEq(count, 1);
}
function test_RevertWhen_VotedTwice() public {
votes.vote(1);
vm.expectRevert("already voted");
votes.vote(1);
}
function testFuzz_VoteDifferentCandidates(uint256 _id) public {
vm.assume(_id < 2);
votes.vote(_id);
(, uint256 count) = votes.candidates(_id);
assertEq(count, 1);
}
}几个关键约定:setUp() 在每个测试用例前跑一次(每个用例拿到全新链状态);vm.expectRevert() 断言”下一次调用必须 revert”,失败路径也能测;vm.assume() 供 fuzz 用例过滤输入。跑起来:
forge test -vvv # -v 越多日志越细
forge coverage # 覆盖率cheatcodes:Foundry 最强的调试武器
vm.* 系列叫 cheatcodes,能做”上帝视角”的事:vm.prank(address) 伪造下一个调用的发送者(测权限)、vm.warp(timestamp) 跳时间(测锁仓)、vm.deal(address, amount) 给账户塞 ETH。权限测试的标准写法:
function test_RevertWhen_NotOwner() public {
vm.prank(address(0xBAD));
vm.expectRevert();
votes.addCandidate("Cheater");
}调试合约内部变量用 console.log(import forge-std 的 console2),它能在 forge test -vvv 输出里打印值,和前端 console.log 的体验一致。
今天的作业:给投票合约补”撤销投票”的测试,覆盖成功路径和失败路径,跑 forge coverage 看看行覆盖能不能到 100%。
读完了点一下,作者能得积分
赞赏
如果这篇内容帮到了你,不妨请作者喝杯咖啡
赞赏
如果这篇内容帮到了你,不妨请作者喝杯咖啡
选个金额
微信扫码扫一下,金额 ¥6
- 扫码后按所选金额支付即可
- 赞赏完全自愿,与积分、红包互不影响
发表评论