harthat插件

编写单元测试首先需要我们在测试钩子中编写发布合约的代码,这意味着,我们需要在每次 beforeEach 钩子中重新发布我们的合约并使其从零状态开始运行。

即使 hardhat 支持在内存中运行区块链并整合了单元测试流程,但这样反复的发布合约也会极大拖慢测试速度。

因此,就单元测试的最佳实践,我向大家推荐 hardhat-deploy 插件。

hardhat-deploy 插件支持使用 evm_snapshot 快速地跳转到某个高度的区块链状态,因此,我们可以使用它在单元测试中维护测试前、中、后以及各种特定高度状态,极大地加快测试速度。

https://github.com/wighawag/hardhat-deploy

注意,引入 hardhat-deploy 插件,需要修改对应的 @nomiclabs/hardhat-ethers 插件来源,这可能会导致在未来的 npm install 中带来版本冲突,如果你遇到了版本冲突,可以使用 npm install --force 跳过版本依赖检查,强制安装两者。

"devDependencies": {
    "@nomiclabs/hardhat-ethers": "npm:hardhat-deploy-ethers",
    "hardhat-deploy": "^0.11.2",
    ...
}

简单来说,在单元测试中,我们可以使用:

await deployments.fixture(['SomeContractName']);

来确保在测试执行前跳回某个状态。如果你需要更加复杂和自定义的 fixture(而非直接跳回某个合约发布后的干净状态)可以使用 deployments.createFixture 来创建自定义 fixture,具体的范例代码和指南可以在这里寻找到:

https://github.com/CodeforDAO/contracts/blob/main/utils/helpers.js#L42

https://github.com/wighawag/hardhat-deploy#creating-fixtures

值得注意的是,使用 hardhat-deploy 插件会同时改变我们发布合约的代码逻辑(正如其名)它支持在 ./deploy 文件夹下编写每个合约的发布脚本。事实上,默认的 deployments.fixture 正会退回这些发布脚本所叙述的合约状态。

不同于 hardhat 默认的发布脚本(使用 npx hardhat run)我们可以使用 hardhat-deploy 插件提供的功能在发布脚本中做更多工作,例如使用 execute 函数立即修改发布后的合约状态,这会在对权限敏感的合约当中非常有用:

const { deploy, execute } = deployments;
const shareGovernor = await deploy('ShareGovernor', {
    contract: 'TreasuryGovernor',
    from: deployer,
    args: [name + '-ShareGovernor', share.address, treasury.address, settings.share.governor],
    log: true,
  });

  // Setup governor roles
  // Both membership and share governance have PROPOSER_ROLE by default
  await execute(
    'Treasury',
    { from: deployer },
    'grantRole',
    PROPOSER_ROLE,
    membershipGovernor.address
  );

除此之外,hardhat-deploy 插件还提供了非常多的 HRE 实用函数,例如getNamedAccounts 能帮助我们命名本地测试账户,而非使用数组下标访问它们。你可以参考该插件的 GitHub 主页了解这些实用功能。

测试覆盖率与gas报告

当合约的单元测试编码到一定程度之后,我们会希望这些合约被发布到某个测试或生产环境(例如测试网络或 ETH 主网)时是否是健壮和低成本的,在此时,我向你推荐两个 hardhat 插件 hardhat-gas-reportersolidity-coverage

hardhat-gas-reporter
hardhat-gas-reporter

hardhat-gas-reporter 插件帮助你了解运行单元测试中部署和执行合约方法消耗的 gas 费用,如果在本地环境变量中提供 COINMARKETCAP_API_KEY,它会自动将这些成本折算为美元或其他法币计价。

https://github.com/cgewecke/hardhat-gas-reporter

solidity-coverage 插件提供单元测试覆盖率报告,这有助于开发团队理解合约是否得到了应有的测试。

https://github.com/sc-forks/solidity-coverage

其他插件

如前所述,hardhat 并不需要以来太多的插件就可以正常工作,满足大部分合约开发团队的需求,我在这里推荐两个其他的使用插件,他们是 @nomiclabs/hardhat-etherscan@tenderly/hardhat-tenderly

这些插件都是可选的,并依赖第三方服务的 API Key,各位读者可以根据自己的情况选择是否使用他们。

hardhat-etherscan 插件将 etherscan 网站的源码 verify 功能整合到发布工作流中,能够将所发布合约的源码和 ABI 都展示在合约地址页面。

https://github.com/NomicFoundation/hardhat

hardhat-tenderly 插件整合了 Tenderly 工作流,后者是一个新兴的 CI/监控 平台,能够帮助我们监控线上合约的状态并提供 debug 建议。

https://tenderly.co/

https://github.com/Tenderly/hardhat-tenderly