ERC20合约测试难题:如何覆盖零地址发起Transfer与Approve场景
解决ERC20合约零地址场景的测试覆盖率问题
我用Hardhat和OpenZeppelin开发ERC20代币项目,测试覆盖率接近100%,但以下两个内部函数的零地址校验分支始终无法覆盖:
_transfer函数中from为零地址的场景_approve函数中owner为零地址的场景
这两个都是极端边缘场景,但为了达成100%覆盖率,需要通过编写测试合约暴露内部函数调用入口的方式触发分支,具体方案如下:
1. 覆盖_transfer中from为零地址的分支
_transfer是内部函数,外部的transfer/transferFrom方法无法直接传入零地址作为from(因为调用者本身是非零地址)。需要编写一个测试合约继承你的ERC20合约,新增公开方法直接调用_transfer:
测试合约示例(Solidity)
// test/helpers/TestERC20.sol pragma solidity ^0.8.0; import "../contracts/YourERC20.sol"; // 替换为你的ERC20合约实际路径 contract TestERC20 is YourERC20 { // 公开方法,直接调用内部的_transfer function testTransferFromZeroAddress(address to, uint256 amount) external { _transfer(address(0), to, amount); } }
测试用例(JavaScript)
it('reverts when transfer from the zero address', async () => { const TestERC20 = await ethers.getContractFactory('TestERC20'); const testContract = await TestERC20.deploy(); // 按你的ERC20构造函数要求传入参数 await testContract.deployed(); const { AddressZero } = ethers.constants; const recipient = ethers.Wallet.createRandom().address; const amount = 1; await expect(testContract.testTransferFromZeroAddress(recipient, amount)) .to.be.revertedWith("ERC20: transfer from the zero address"); });
2. 覆盖_approve中owner为零地址的分支
同理,外部approve方法的owner是调用者本身(非零地址),需要通过测试合约暴露方法直接调用_approve:
测试合约示例(Solidity)
// test/helpers/TestERC20.sol pragma solidity ^0.8.0; import "../contracts/YourERC20.sol"; contract TestERC20 is YourERC20 { // 新增方法,直接调用内部的_approve function testApproveFromZeroAddress(address spender, uint256 amount) external { _approve(address(0), spender, amount); } }
测试用例(JavaScript)
it('reverts when approve from the zero address', async () => { const TestERC20 = await ethers.getContractFactory('TestERC20'); const testContract = await TestERC20.deploy(); // 按你的ERC20构造函数要求传入参数 await testContract.deployed(); const { AddressZero } = ethers.constants; const spender = ethers.Wallet.createRandom().address; const amount = 1; await expect(testContract.testApproveFromZeroAddress(spender, amount)) .to.be.revertedWith("ERC20: approve from the zero address"); });
补充说明
- 测试合约仅用于测试环境部署,无需上链;
- 如果你的ERC20合约使用了OpenZeppelin的自定义错误(如
ERC20InvalidSender),请将断言中的revertedWith替换为revertedWithCustomError,和你现有测试风格保持一致。
内容的提问来源于stack exchange,提问作者Mati
相关产品推荐
相关产品推荐

