如何用Hardhat测试call函数回滚分支以实现全代码覆盖?
如何触发Solidity中call调用的失败分支以实现100%代码覆盖率?
问题场景
我希望实现100%代码覆盖率,但某函数中的call调用分支无法覆盖,相关Solidity代码如下:
(bool sent, bytes memory data) = _to.call{value: msg.value}(""); require(sent, "Failed to send Ether");
尝试通过修改收款地址余额来触发失败:
await network.provider.send("hardhat_setBalance", [ "...address...", "0x1000" ]);
但未触发require语句,反而报错:sender doesn't have enough funds to send tx。
技术栈:Hardhat、Solidity、ethers.js、chai.js
解决方案
方法1:使用无法接收ETH的合约地址作为收款方
当收款地址是没有receive或payable fallback函数的合约时,发送ETH的call调用会返回false,从而触发require的失败分支。
步骤:
- 在测试文件中编写一个无收款能力的测试合约:
contract NonPayableReceiver { // 不定义receive或payable fallback函数,无法接收ETH }
- 部署该合约,将其地址作为
_to传入目标函数:
const NonPayableReceiver = await ethers.getContractFactory("NonPayableReceiver"); const nonPayableReceiver = await NonPayableReceiver.deploy(); await nonPayableReceiver.deployed(); // 调用目标函数,传入该合约地址作为收款方 await expect(targetContract.sendEther(nonPayableReceiver.address, { value: ethers.utils.parseEther("0.1") })) .to.be.revertedWith("Failed to send Ether");
这样call调用会返回false,触发require语句,覆盖失败分支。
方法2:解决「sender余额不足」的报错
你之前的报错是因为发送交易的测试账户余额不足,和收款地址余额无关。可以先给测试账户充值:
// 给测试账户充值ETH await network.provider.send("hardhat_setBalance", [ await ethers.provider.getSigner().getAddress(), ethers.utils.parseEther("10").toHexString() ]);
注意:这只是解决报错问题,要触发sent=false仍需配合方法1的思路——普通外部账户(EOA)总能接收ETH,call不会返回false。
方法3:模拟gas不足触发失败(进阶)
通过限制调用时的gas量,让call因gas不足返回false:
// 调用时设置极低的gasLimit,导致call执行失败 await expect(targetContract.sendEther(recipientAddress, { value: ethers.utils.parseEther("0.1"), gasLimit: 21000 // 低于call执行所需的最小gas })) .to.be.revertedWith("Failed to send Ether");
这种方法需要精确控制gas量,稳定性不如方法1。
内容的提问来源于stack exchange,提问作者MiKa
相关产品推荐
相关产品推荐

