HackQuest_Forge_Std
uwupu 啦啦啦啦啦

Forge

即forge标准库。为foundry框架提供一套丰富的辅助合约集合。

forge-std

forge-std旨在简化和加速智能合约的测试编写过程,提升用户体验。

Usage

1
2
3
4
5
import "forge-std/Test.sol";

contract ContractTest is Test {
// 测试代码
}

Function

  • 访问Hevm:通过vm实例,可以直接模拟区块链的状态和行为。
  • 断言与日志记录。
  • 标准库功能。

Import Example

1
2
3
4
import "forge-std/Vm.sol";
import "forge-std/console.sol";
// 或使用 console2.sol,它提供了 console.sol 的补丁
import "forge-std/console2.sol";

组成

  • std logs: 基于DSTest库的日志事件功能进行了扩展。
  • std assertions:对DSTest断言函数进行了扩展
  • std cheats: Std Cheats 为 Forge cheatcodes 提供了安全的封装,改善了开发体验。通过在测试合约中调用它们,可以轻松实现身份伪装、账户余额设置等操作。
  • std errors:Std Errors 提供了对常见 Solidity 错误和回退的封装,与 expectRevert cheatcode 结合使用时,无需记住 Solidity 内部的 panic 代码。
  • std storage: Std Storage 简化了合约存储的操作,使得查找和修改特定变量的存储位置变得简单直接。
  • std math: Std Math 库提供了 Solidity 中未提供的有用数学函数,为开发者提供了更多的数学运算支持。
flowchart LR
    r[forge-std]
    n1[std logs]
    n2[std assertions]
    n3[std cheats]
    n4[std errors]
    n5[std storage]
    n6[std math]
    r --> n1
    r --> n2
    r --> n3
    r --> n4
    r --> n5
    r --> n6

cheatcode介绍

cheatcode:作弊码。

可以操作区块链的状态,测试特定的还原操作和事件。

作弊码允许开发者测试中执行一些非标准操作。

比如:更改区块号,修改调用者身份,修改fork(切换网络)等。

在forge标准库的Test合约中,可以通过vm实现访问作弊码。

Usage

  • vm.prank,身份切换,暂时切换调用者身份;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
pragma solidity 0.8.10;

import "forge-std/Test.sol";

contract OwnerUpOnlyTest is Test {
OwnerUpOnly upOnly;

function setUp() public {
upOnly = new OwnerUpOnly();
}

function testFail_IncrementAsNotOwner() public {
vm.prank(address(0));
upOnly.increment();
}
}
  • vm.createFork(RPC_URL):创建一个Fork实例;
  • vm.selectFork(Fork_实例):切换到指定Fork。
  • vm.ffi(ARGS):运行外部命令,返回执行结果。

(下面的内容讲的有些乱,挖坑)

vm.expectEmit

Foundry编写测试

Usage

  1. 编写测试类

    1. 导入Test.sol
    2. 编写测试类:contract <ContractName> is Test
    3. 编写setUp:function setUp() public {}
    4. 编写测试逻辑:这里指的是数个方法,以test/testFail开头
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    pragma solidity ^0.8.13;

    //1. 导入Test.sol
    import "forge-std/Test.sol";

    //2. 编写测试类
    contract ContractBTest is Test {
    uint256 testNumber;//
    //3. 数据初始化
    function setUp() public {
    testNumber = 42;
    }

    //4. 测试逻辑
    function test_Subtract43() public {

    vm.expectRevert(stdError.arithmeticError);
    console2.log("testNumber = %d", testNumber);

    testNumber -= 43;
    }
    }
  2. 执行测试(参照:上一个日记

    1
    forge test

测试逻辑编写

  • 测试逻辑由数个test方法组成。

  • 对于test方法,必须以test / testFail开头。

  • 对于forge的测试过程,会测试所有的test方法,最后输出通过的数量和不通过的数量。

对于test / testFail

使用test标记的方法,如果该方法在运行过程中没有报错,则表示通过;

testFail反之,如果报错了,则通过,否则为测试失败/不通过

更精确的测试处理

1
2
3
4
function test_MyRevertCase() public {
vm.expectRevert(SomeError.selector);
// 可能触发SomeError的操作
}

vm.expectRevert(<Error>.selector);表示接下来的操作必须发生指定错误才能通过测试。

OTHER

  • 测试函数必须声明为external或public
  • 使用setUp设定共享设置,减少重复代码。

自动多次随机数据测试Fuzz Testing

Fuzz Testing可以为测试方法提供随机参数,并进行多次测试。

Usage

  • 方法名以testFuzz开头
1
function testFuzz_Withdraw(uint256 amount) public {}
  • 限定参数范围:设定参数类型使用vm.assume作弊码
1
2
3
4
5
6
7
function testFuzz_Withdraw(uint96 amount) public {}// uint96限定测试范围

//使用vm.assume作弊码
function testFuzz_Withdraw(uint96 amount) public {
vm.assume(amount > 0.1 ether);
// 测试逻辑...
}
 评论