SuperAPI AI 工具集

foundry

测试与质量

查看项目

更新于 10-16 10:12

技能介绍

用途

Foundry 技能用于编写基于 Foundry 的测试与脚本,覆盖 Solidity 智能合约的测试编写、部署脚本编写、gas 性能测量等任务。其触发场景包括 foundry testing、write test、fuzz test、fork test、invariant test、deploy script、gas benchmark 等。

主要能力

该技能提供多类测试与脚本的规则和模式,并附带参考文档:

  • 测试基础设施:常量、默认值、mock,设置测试时阅读。
  • cheatcode 模式:常用 cheatcode 模式,使用 vm cheatcode 时阅读。
  • 不变量模式:handlers、stores、invariants,编写不变量测试时阅读。
  • 形式化验证:Halmos、Certora、符号执行,证明正确性时阅读。
  • 部署脚本:脚本模式、验证,编写部署脚本时阅读。
  • 部署检查清单:主网上线前部署步骤,生产部署前阅读。
  • gas 基准测试:快照、性能分析、CI,测量 gas 性能时阅读。
  • Sablier 约定:Sablier 特定模式,在 Sablier 仓库中工作时阅读。

测试类型与目录

  • 集成测试(具体):目录 tests/integration/concrete/,命名 *.t.sol,基于 BTT 的具体测试。
  • 模糊测试:目录 tests/integration/fuzz/,命名 *.t.sol,基于属性的测试。
  • 分叉测试:目录 tests/fork/,命名 *.t.sol,主网状态测试。
  • 不变量测试:目录 tests/invariant/,命名 Invariant*.t.sol,有状态协议属性。
  • 脚本:目录 scripts/solidity/,命名 *.s.sol,部署/初始化。

使用方式

集成测试(具体)

命名约定:test_RevertWhen_{Condition} 表示输入导致回滚;test_RevertGiven_{State} 表示状态导致回滚;test_When_{Condition} 表示成功路径。 规则:堆叠 modifiers 以记录 BTT 路径(modifiers 通常为空,仅用于记录路径);在操作之前期望事件——vm.expectEmit() 然后调用函数;在操作之后断言状态;对常见模式使用 revert helpers(expectRevert_DelegateCall、expectRevert_Null);断言中使用命名参数——assertEq(actual, expected, "description")。 Mock 规则:所有 mock 放在 tests/mocks/;每个场景一个 mock(而非一个巨型 mock);命名:*Good、*Reverting、*InvalidSelector、*Reentrant。

模糊测试

命名约定:testFuzz_{FunctionName}_{Scenario}。 规则:先 bound 后 assume——_bound() 比 vm.assume() 更高效;按依赖顺序 bound——先独立参数,再依赖参数;不要硬编码带验证约束的参数;在 NatSpec 中记录被模糊测试的场景。 Bounding 模式示例: // 1. 先 bound 独立参数 cliffDuration = boundUint40(cliffDuration, 0, MAX - 1); // 2. 根据约束 bound 依赖参数 totalDuration = boundUint40(totalDuration, cliffDuration + 1, MAX);

分叉测试

规则:使用 vm.createSelectFork("ethereum") 创建分叉;使用 deal() 给测试用户发放代币;对 USDC/USDT 使用 assumeNoBlacklisted();对非标准代币(USDT)使用 forceApprove()。 代币特性:USDC/USDT 存在黑名单问题,解决方式是 assumeNoBlacklisted();USDT 非标准,解决方式是 forceApprove();fee-on-transfer 存在余额差异,解决方式是检查实际收到金额。

不变量测试

架构:tests/invariant/ 下包含 handlers/(状态操作,用有界参数调用函数)、stores/(状态跟踪,记录总量、ID)、Invariant.t.sol。 规则:只 target handlers——targetContract(address(handler));排除协议合约——excludeSender(address(vault));使用 stores 跟踪总量以供不变量断言;若前置条件不满足,在 handlers 中提前返回。

Solidity 脚本

规则:继承带 broadcast modifier 的 BaseScript;使用环境变量:ETH_FROM、MNEMONIC;先模拟后广播。 命令:

模拟

forge script scripts/Deploy.s.sol --sig "run(...)" ARGS --rpc-url $RPC

广播

forge script scripts/Deploy.s.sol --sig "run(...)" ARGS --rpc-url $RPC --broadcast --verify

运行测试

按类型

forge test --match-path "tests/integration/concrete/" forge test --match-path "tests/fork/" forge test --match-contract Invariant_Test

特定测试

forge test --match-test test_WhenCallerRecipient -vvvv

更多次运行的模糊测试

forge test --match-test testFuzz_ --fuzz-runs 1000

覆盖率

forge coverage --report lcov

调试

详细级别:-v 显示失败测试的日志;-vv 显示所有测试的日志;-vvv 显示失败的堆栈跟踪;-vvvv 显示堆栈跟踪与设置跟踪;-vvvvv 显示完整执行跟踪。 控制台日志: import { console2 } from "forge-std/console2.sol"; console2.log("value:", someValue); console2.log("address:", someAddress); console2.logBytes32(someBytes32); 调试命令:

跟踪特定失败测试

forge test --match-test test_MyTest -vvvv

某测试的 gas 报告

forge test --match-test test_MyTest --gas-report

在交互式调试器中调试

forge debug --debug tests/MyTest.t.sol --sig "test_MyTest()"

检查存储布局

forge inspect MyContract storage-layout 调试提示:用 vm.label(addr, "Recipient") 标记地址以便阅读跟踪;在 revert 前用日志检查状态;用 --match-test 隔离失败;用 --gas-report 对比 gas 以发现意外开销;用 vm.snapshot() / vm.revertTo() 隔离状态变化。

最佳实践摘要

使用 Defaults/Constants 中的常量——不要硬编码;专用 mock——每个场景一个,全部放在 tests/mocks/;modifiers 放在 Modifiers.sol——集中管理 BTT 路径 modifiers;用 vm.label() 标记地址以便跟踪;事件在操作之前——vm.expectEmit() 然后调用;先 bound 后 assume——更高效。

外部参考

Foundry Book

示例调用

可用以下提示测试该技能:集成测试:“为 withdraw 编写一个具体测试,当 amount 超过余额时期望 Errors.Flow_Overdraw”;模糊测试:“为 deposit 创建一个模糊测试,将 amount 界定在 1 与 type(uint128).max 之间”;分叉测试:“为主网上的 USDC 存款编写一个带黑名单处理的分叉测试”;不变量测试:“为 deposit 和 withdraw 函数创建一个不变量 handler”;部署脚本:“为 SablierFlow 编写一个带验证的部署脚本”。

限制

其他信息暂未提供。