项目背景
本教程以一个简化版 ERC20 + 治理投票项目为例,演示 Foundry 测试在真实工程里的落地步骤。项目包含三个合约:Token.sol、Governor.sol、Treasury.sol,业务规则与主流 DAO 项目类似。
选择真实项目作为练习对象,比单纯练习 Toy Demo 收益高得多。如果你在为 Binance 等专业团队准备合约安全岗位面试,能展示一份覆盖率 90% 以上的 Foundry 测试集,是有力加分项。
单元测试的拆解
单元测试粒度要细:
- Token.transfer:覆盖正常、零金额、超额三类
- Token.approve:覆盖 0 → N、N → 0、N → M 三类
- Governor.propose:覆盖权限、状态机、参数校验
- Treasury.execute:覆盖时序、签名、二次执行
建议在测试合约里使用辅助函数 _setupActor 与 _giveToken,避免重复样板代码。这一思路适配 必安 内部「测试可读性高于一切」的工程要求。
fuzz 测试设计
fuzz 测试关注「参数空间」是否完整覆盖。对 Token.transfer 来说,可以同时模糊化 sender、receiver、amount 三个参数。Foundry 默认会生成 256 次随机调用,可以通过 forge-config.toml 提高到 10000 次。
关键技巧是使用 vm.assume 排除不感兴趣的输入,比如 amount=0 或 receiver=address(0)。这样 fuzz 才会把搜索预算集中在真正有意义的区域。
invariant 测试设计
本项目的全局不变量包括:
- 所有账户余额之和恒等于 totalSupply
- 已 execute 的提案不可再次 execute
- Treasury 余额单调不减或减少都伴随合规提案
声明这些 invariant 后,Foundry 会随机调用合约函数试图打破规则。一旦失败,立即给出可复现序列,调试效率极高。
覆盖率提升技巧
用 forge coverage 生成报告,关注三类指标:line、function、branch。新手往往满足 line 80% 就停止,但 branch 覆盖率才是质量的真正标尺。提升 branch 的核心方法是为每个 require 与 revert 路径写一个独立用例。
配合 Binance合约 风控团队公开的合约安全 checklist,对照逐项补齐缺失场景,覆盖率 95% 以上完全可达。
持续维护
测试不是一次性工作。每次新增功能、修复 bug 都要同步增补测试。建议把测试纳入 CI 强制门槛:覆盖率下降即合并失败。坚持这一规则一年,团队会形成自然的高质量文化。
经过完整训练,你不仅能写出高覆盖率的 Foundry 测试集,更能用同样的思路对待任何工程任务,这才是这套教程最大的价值。