You need to enable JavaScript to run this app. 如何在Solidity中正确测试智能合约的异常场景?
优惠活动
大模型
产品
解决方案
定价
更多

如何在Solidity中正确测试智能合约的异常场景?

问题原因

你删掉require校验后测试依然通过,核心原因是你大概率使用了Solidity 0.8及以上版本:
0.8版本开始Solidity默认内置算术溢出/下溢检查,当发送方余额小于转账金额时,执行balanceOf[msg.sender] -= _value会自动触发REVERT错误,不需要你手动写require校验。所以你删除require语句后,超余额转账时代码依然会抛出revert,测试自然会一直判定为通过。

如果要复现你预期的「删除require后测试失败」的效果,可以用unchecked块关闭算术检查,修改后的transfer代码如下:

function transfer(address _to, uint256 _value) public returns (bool success) {
    // 移除require,用unchecked关闭溢出检查,超余额转账也不会触发revert
    unchecked {
        balanceOf[msg.sender] -= _value;
        balanceOf[_to] += _value;
    }
    emit Transfer(msg.sender, _to, _value);
    return true;
}

此时再跑原来的测试,就会因为代码没有抛出revert而测试失败,符合你的预期。


正确可靠的测试方式

1. 匹配revert原因,避免误判

原来的测试只能验证函数抛出了revert,但没法区分是你写的余额校验生效,还是算术溢出自动抛出的revert。建议给require加自定义错误信息,测试时匹配错误信息:
首先修改合约的require语句:

require(balanceOf[msg.sender] >= _value, "Insufficient balance");

然后修改测试代码,用更适配revert场景的truffleAssert.reverts方法:

const truffleAssert = require("truffle-assertions");

it("should revert transfer if the amount is larger than the sender's balance", async () => {
  const instance = await MyToken.deployed();
  // 明确指定调用方,同时匹配revert的错误信息,避免误判
  await truffleAssert.reverts(
    instance.transfer(accounts[1], 10000000, {from: accounts[0]}),
    "Insufficient balance"
  );
});

2. 补充状态校验,覆盖全场景

除了校验错误抛出逻辑之外,还要补充两类状态校验,确保逻辑完全符合预期:

  • 转账成功场景:校验发送方余额减少、接收方余额增加的数值完全正确
  • 转账失败场景:校验双方余额都没有发生任何变动

内容的提问来源于stack exchange,提问作者Hidayat Rzayev

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.19 18:45:03