通过一个 Counter 例子认识 Forge 的测试流程,再区分 Cast、Anvil 和 Chisel 的用途。
本文由个人备赛旧笔记整理,整理日期为 2026-11-03。命令、输出和测试记录属于当时的实验环境;本文未重新搭建整套环境验证。
阅读前先确认
Foundry 是以太坊开发工具集;这里的 Solidity 测试不能直接作为 Rust ink! 合约测试。公网 RPC 是历史练习地址,复现时自行配置可用端点。
基本
主要组件:
- Forge - 以太坊测试框架
- Cast - 以太坊 RPC 客户端
- Anvil - 本地以太坊节点
- Chisel - Solidity REPL
forge使用指令
会自动编译合约并运行测试
cast 用于交互链上
1 2 3
| (base) ┌──(cure㉿LAPTOP-CMAM5D0J)-[~/foundry-test/Counter] └─$ cast call 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 "balanceOf(address)" 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 --rpc-url https://reth-ethereum.ithaca.xyz/rpc 0x000000000000000000000000000000000000000000000000000000174876e800
|
- 合约地址: 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 (WETH)
- 查询方法: balanceOf(address)
- 目标地址: 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 (vitalik.eth)
- 返回数据: 0x000000000000000000000000000000000000000000000000000000174876e800
anvil 原地开启以太坊节点
一次简单的测试
合约代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13;
contract Counter { uint256 public number;
function setNumber(uint256 newNumber) public { number = newNumber; } // set number
function increment() public { number++; } }
|
测试的合约代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13;
import {Test} from "forge-std/Test.sol"; import {Counter} from "../src/Counter.sol";
contract CounterTest is Test { Counter public counter;
function setUp() public { counter = new Counter(); // Counter 合约实体
// 设置数字0 counter.setNumber(0); }
function test_Increment() public { // 测试++ counter.increment(); // 是否包含1 assertEq(counter.number(), 1); }
function testFuzz_SetNumber(uint256 x) public { counter.setNumber(x); // 测试设置数字 assertEq(counter.number(), x); }
function testBoundary(uint256 x) public { // 使用更严格的假设避免溢出 vm.assume(x < type(uint256).max - 1);
counter.setNumber(x); uint256 initialValue = counter.number();
counter.increment(); assertEq(counter.number(), initialValue + 1); }
function test_ZeroIncrement() public { // 测试从0开始多次递增 counter.setNumber(0);
for (uint256 i = 0; i < 10; i++) { uint256 before = counter.number(); counter.increment(); assertEq(counter.number(), before + 1); } } }
|
结果
1 2 3 4 5 6 7 8 9 10 11 12 13
| (base) ┌──(cure㉿LAPTOP-CMAM5D0J)-[~/foundry-test/Counter] └─$ forge test [⠊] Compiling... No files changed, compilation skipped
Ran 4 tests for test/Counter.t.sol:CounterTest [PASS] testBoundary(uint256) (runs: 256, μ: 34794, ~: 34794) [PASS] testFuzz_SetNumber(uint256) (runs: 257, μ: 28790, ~: 29333) [PASS] test_Increment() (gas: 28827) [PASS] test_ZeroIncrement() (gas: 63442) Suite result: ok. 4 passed; 0 failed; 0 skipped; finished in 4.59ms (6.87ms CPU time)
Ran 1 test suite in 5.17ms (4.59ms CPU time): 4 tests passed, 0 failed, 0 skipped (4 total tests)
|
复现时的自查
- 记录自己实际使用的版本、配置和缺失依赖。
- 对照本篇关键步骤,保存命令或调用结果,并解释结果代表什么。
- 对失败或与原记录不一致的情况保留错误信息,不把历史输出当成自己的验证结果。
返回系列导读 · 上一篇 · 下一篇