如何在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
相关产品推荐
相关产品推荐

