mockCall

签名

function mockCall(address where, bytes calldata data, bytes calldata retdata) external;
function mockCall(
    address where,
    uint256 value,
    bytes calldata data,
    bytes calldata retdata
) external;

描述

如果调用数据严格或宽松匹配 data,则模拟对地址 where 的所有调用,并返回 retdata

当对 where 进行调用时,首先检查调用数据是否与 data 完全匹配。 如果不匹配,则检查调用数据是否部分匹配,匹配从调用数据的第一个字节开始。

如果找到匹配项,则从调用返回 retdata

使用第二个签名,我们可以模拟具有特定 msg.value 的调用。在存在歧义的情况下,calldata 匹配优先于 msg.value

模拟的调用有效,直到调用 clearMockedCalls

ℹ️ 注意

对模拟地址的调用可能会引发还原,如果地址上没有代码。 这是因为 Solidity 在某些合约调用之前插入了 extcodesize 检查。

为了规避这一点,如果模拟地址没有代码,请使用etch cheatcode。

ℹ️ 内部调用

此作弊码目前无法在内部调用上使用。请参见问题 #432

例子

模拟精确调用:

function testMockCall() public {
    vm.mockCall(
        address(0),
        abi.encodeWithSelector(MyToken.balanceOf.selector, address(1)),
        abi.encode(10)
    );
    assertEq(IERC20(address(0)).balanceOf(address(1)), 10);
}

模拟整个函数:

function testMockCall() public {
    vm.mockCall(
        address(0),
        abi.encodeWithSelector(MyToken.balanceOf.selector),
        abi.encode(10)
    );
    assertEq(IERC20(address(0)).balanceOf(address(1)), 10);
    assertEq(IERC20(address(0)).balanceOf(address(2)), 10);
}

模拟具有给定 msg.value 的调用:

function testMockCall() public {
    assertEq(example.pay{value: 10}(1), 1);
    assertEq(example.pay{value: 1}(2), 2);
    vm.mockCall(
        address(example),
        10,
        abi.encodeWithSelector(example.pay.selector),
        abi.encode(99)
    );
    assertEq(example.pay{value: 10}(1), 99);
    assertEq(example.pay{value: 1}(2), 2);
}

模拟公开的参数:

contract Example {
    uint256 public number = 10;
}

contract ExampleTest is Test {
    Example public example;

    function setUp() public {
        example = new Example();
    }

    function testMockPublicVariable() public {
        assertEq(example.number(), 10);
        vm.mockCall(
            address(example),
            abi.encodeWithSelector(bytes4(keccak256("number()"))),
            abi.encode(5)
        );
        assertEq(example.number(), 5);
    }
}