Allureluoli 二月FEBRUARY

区块链国赛笔记 09|Foundry 入门:工具分工与 Counter 测试

通过一个 Counter 例子认识 Forge 的测试流程,再区分 Cast、Anvil 和 Chisel 的用途。

本文由个人备赛旧笔记整理,整理日期为 2026-11-03。命令、输出和测试记录属于当时的实验环境;本文未重新搭建整套环境验证。

阅读前先确认

Foundry 是以太坊开发工具集;这里的 Solidity 测试不能直接作为 Rust ink! 合约测试。公网 RPC 是历史练习地址,复现时自行配置可用端点。

基本

主要组件:

  • Forge - 以太坊测试框架
  • Cast - 以太坊 RPC 客户端
  • Anvil - 本地以太坊节点
  • Chisel - Solidity REPL

forge使用指令

1
forge test

会自动编译合约并运行测试

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
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)

复现时的自查

  • 记录自己实际使用的版本、配置和缺失依赖。
  • 对照本篇关键步骤,保存命令或调用结果,并解释结果代表什么。
  • 对失败或与原记录不一致的情况保留错误信息,不把历史输出当成自己的验证结果。

返回系列导读 · 上一篇 · 下一篇

评论COMMENTS