本站域名已变更为https://blog.67v.cn,全新首页即将发布,尽情期待!!!

第六天:合约测试与调试——让代码先证明自己

合约一旦部署,改代码 = 重新部署整套(状态迁移),bug 的代价远高于普通软件。所以”先写测试”在合约开发里不是美德,是生存技能。今天给第五天的投票合约补上完整的测试。

用 Solidity 写测试

code
// 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 用例过滤输入。跑起来:

code
forge test -vvv          # -v 越多日志越细
forge coverage           # 覆盖率

cheatcodes:Foundry 最强的调试武器

vm.* 系列叫 cheatcodes,能做”上帝视角”的事:vm.prank(address) 伪造下一个调用的发送者(测权限)、vm.warp(timestamp) 跳时间(测锁仓)、vm.deal(address, amount) 给账户塞 ETH。权限测试的标准写法:

code
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%。

读完了点一下,作者能得积分

赞赏

如果这篇内容帮到了你,不妨请作者喝杯咖啡

分享 微博 QQ

发表评论

发表评论