Foundry智能合约测试设计流程
Foundry智能合约测试设计流程
通用测试流程
-
测试环境搭建:
- 创建测试合约类,继承自
Test
- 在
setUp
函数中初始化被测合约和测试账户 - 准备必要的模拟数据和环境
- 创建测试合约类,继承自
-
测试用例编写:
- 每个测试函数测试一个具体场景
- 测试函数名应清晰表达测试目的
- 遵循AAA模式:Arrange(准备)、Act(执行)、Assert(断言)
-
测试执行与调试:
- 使用
forge test
运行测试 - 针对失败的测试进行调试和修复
- 使用
四种测试维度详解
1. 单元测试 (Unit Tests)
目的:测试单个合约功能的正确性,隔离外部依赖。
应该测试的方法:
fund()
的基本捐赠功能- 各种修饰器(
onlyOwner
,canWithdraw
,canClaim
,notPaused
) pause()
/unpause()
功能getUserInfo()
等查询函数calculateTokens()
内部计算逻辑
测试原则:
- 模拟(Mock)外部依赖,如价格预言机
- 每个函数的正常路径和异常路径都要测试
- 测试不同的边界条件
命名规则:
test{函数名}_{测试场景}()
例如:testFund_SuccessfulContribution()
或testFund_RevertWhenPresaleEnded()
2. 集成测试 (Integration Tests)
目的:测试多个合约组件之间的交互。
应该测试的方法:
TokenPresale
与PriceConverter
的集成withdrawETH()
与enableClaim()
的联动- 完整的捐赠、提现、代币计算、领取流程
测试原则:
- 少量或不模拟外部依赖
- 测试跨合约调用链
- 测试状态变化的连续性
命名规则:
test{流程名}_{测试场景}()
例如:testFundWithdrawClaim_CompleteFlow()
3. 分叉测试 (Forked Tests)
目的:在真实网络的快照上测试合约与实际环境的交互。
应该测试的方法:
getLatestETHPriceInUSD()
与真实Chainlink预言机交互- 任何依赖外部合约或服务的功能
测试原则:
- 使用
forge test --fork-url <RPC_URL>
- 测试与实际外部服务的交互
- 验证在真实环境中的行为
命名规则:
testFork{功能名}_{测试场景}()
例如:testForkPriceConversion_AccurateUsdValue()
4. 阶段测试 (Staging Tests)
目的:模拟生产环境,在部署前进行最终验证。
应该测试的方法:
- 完整的用户流程(端到端测试)
- 极端场景测试
- 涉及多个用户和时间流逝的复杂场景
测试原则:
- 不模拟任何依赖,使用真实环境
- 测试真实的用户交互场景
- 测试合约在不同状态转换下的行为
命名规则:
testStage{场景名}_{期望结果}()
例如:testStageFullPresaleCycle_SuccessfulCompletion()
测试优先级
-
首先测试核心功能:
fund()
捐赠逻辑- 价格转换功能
- 提现功能
-
然后测试安全控制:
- 访问控制(修饰器)
- 紧急暂停功能
- 边界条件处理
-
最后测试辅助功能:
- 查询功能
- 特殊场景
测试用例编写原则
- 完整性:覆盖所有关键功能和边界情况
- 独立性:每个测试应该独立运行,不依赖其他测试
- 可读性:测试代码应清晰表达测试目的和预期结果
- 稳定性:避免非确定性行为,如依赖特定时间或区块
- 效率:测试应该快速运行,避免不必要的操作
示例测试结构
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;import {Test, console} from "forge-std/Test.sol";
import {TokenPresale} from "../../src/TokenPresale.sol";
import {PriceConverter} from "../../src/PriceConverter.sol";contract TokenPresaleTest_Unit is Test {TokenPresale tokenPresale;address owner = address(0x1);address user1 = address(0x2);function setUp() public {vm.startPrank(owner);tokenPresale = new TokenPresale();vm.stopPrank();// 模拟价格预言机返回// ...}function testFund_SuccessfulContribution() public {// Arrangevm.prank(user1);uint256 contributionAmount = 1 ether;// Actvm.deal(user1, contributionAmount);tokenPresale.fund{value: contributionAmount}();// AssertassertTrue(tokenPresale.hasContributed(user1));assertEq(tokenPresale.userUsdContributed(user1), contributionAmount.getLatestETHPriceInUSD());}// 更多测试...
}
按照这个框架,可以有条理地开发四个维度的测试,确保智能合约的功能正确性和安全性。