You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Foundry中模拟返回不同结果的外部调用?

在Foundry中模拟同一外部调用返回不同结果

Foundry完全支持模拟同一外部调用多次返回不同结果的场景,下面提供几种实用的实现方式:

方法1:使用带调用次数限制的vm.mockCall

vm.mockCall有重载版本支持指定count参数,限制该mock仅生效指定次数。通过两次调用vm.mockCall分别设置不同返回值和生效次数,就能实现两次调用返回不同结果:

function testCalculateDelta() public {
    address calculatorAddr = address(0x123); // 目标合约地址
    TargetContract target = new TargetContract();

    // 第一次调用`getValue`返回10,仅生效1次
    vm.mockCall(
        calculatorAddr,
        1, // 生效次数
        abi.encodeWithSelector(ICalculator.getValue.selector),
        abi.encode(int256(10))
    );

    // 第二次调用`getValue`返回20,仅生效1次
    vm.mockCall(
        calculatorAddr,
        1,
        abi.encodeWithSelector(ICalculator.getValue.selector),
        abi.encode(int256(20))
    );

    // 执行测试并验证结果
    int256 delta = target.getDelta(calculatorAddr);
    assertEq(delta, 10);
}

方法2:使用vm.expectCall指定每次调用的返回值

vm.expectCall可以预期外部调用的发生,并绑定对应返回值。连续调用两次vm.expectCall,分别匹配两次getValue的调用场景:

function testCalculateDeltaWithExpectCall() public {
    address calculatorAddr = address(0x123);
    TargetContract target = new TargetContract();

    // 预期第一次`getValue`调用,返回10
    vm.expectCall(
        calculatorAddr,
        abi.encodeWithSelector(ICalculator.getValue.selector)
    ).returns(abi.encode(int256(10)));

    // 预期`update`调用(可选,用于验证该方法确实被执行)
    vm.expectCall(
        calculatorAddr,
        abi.encodeWithSelector(ICalculator.update.selector)
    );

    // 预期第二次`getValue`调用,返回20
    vm.expectCall(
        calculatorAddr,
        abi.encodeWithSelector(ICalculator.getValue.selector)
    ).returns(abi.encode(int256(20)));

    // 执行测试
    int256 delta = target.getDelta(calculatorAddr);
    assertEq(delta, 10);
}

方法3:自定义模拟合约

如果需要更复杂的状态逻辑,可编写自定义模拟合约,通过内部状态控制不同调用的返回值:

// 模拟ICalculator接口的合约
contract MockCalculator is ICalculator {
    uint256 private _callCount;
    int256 public preUpdateValue;
    int256 public postUpdateValue;

    constructor(int256 pre, int256 post) {
        preUpdateValue = pre;
        postUpdateValue = post;
    }

    function getValue() external returns (int256) {
        _callCount++;
        return _callCount == 1 ? preUpdateValue : postUpdateValue;
    }

    function update() external {} // 空实现,仅满足接口要求
}

// 测试函数
function testCalculateDeltaWithMockContract() public {
    MockCalculator mockCalc = new MockCalculator(10, 20);
    TargetContract target = new TargetContract();

    int256 delta = target.getDelta(address(mockCalc));
    assertEq(delta, 10);
}

各方法适用场景

  • 方法1适合简单的固定次数不同返回值场景,代码简洁;
  • 方法2适合需要验证外部调用顺序、次数的场景;
  • 方法3适合需要模拟复杂状态变化或逻辑的场景,扩展性更强。

内容的提问来源于stack exchange,提问作者Costin Razvan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 04:15:36