🎃

[solidity]hardhatによるコントラクトのテストTips

2022/07/17に公開

最近NFTのコントラクトに対するテストをする機会があったのでその時に実践したTipsを紹介します。

はじめに

hardhatって何?

solidityをローカルで実行するための環境です。
node.js上で動作するため、npmやyarnを用いて簡単に環境構築できます。

solidityのテストを実際のEthereumネットワークにデプロイして実施すると、結構馬鹿にならないガス代が必要になるため、ローカルでテストできるhardhatは重宝します。

ちょっと動かしてみたいなという方は公式ドキュメントのチュートリアルをやってみてください。

Tips

テスト用アドレス数(signer数)を変える

テスト用のアカウントは以下のように取得することができます。

const { ethers } = require("hardhat");
const [owner, ...rest] = await ethers.getSigners();

この時、デフォルトでは取得できるsignerは20です。
しかし、ガス代の見積もり、実行時間の見積もりをするためにもっと多くのsignerを用意したいケースもあると思います。
その場合は、以下のようにhardhat.config.jsにてhardhatネットワークの設定を明示的に入れることで増減させることができます。

hardhat.config.js
module.exports = {
  solidity: "0.8.4",
  networks: {
    hardhat: {
      accounts: {
        count: 100 // ←ここの数字を指定する。
      }
    }
}

ただし、アカウント数を多くすると実行時間がかかるので必要な時のみ増やすのが望ましいです。
逆にデフォルトの20も必要ない場合は、小さな値にすることで若干テスト時間を短縮することができます。

Fixtureを使って実行時間を短縮する

Fixtureを使用すると関数実行後の状態を保存しておいてすぐに再利用することができます。
そのため、コントラクトのデプロイやアドレスの用意はFixtureで行うことでテスト時間を短縮できます。

test.js
  // 全テストで共通する初期処理を実行する。
  async function deployFixture() {
    const [owner, ...rest] = await ethers.getSigners();
    const MYCONTRACT = await ethers.getContractFactory("MYCONTRACT");
    const myContract = await MYCONTRACT.connect(owner).deploy();

    return { myContract, owner, rest };
  }
  
  const afterUintValue = 99999
  
  it("some test", async function () {
    // loadFixtureでdeployFixture()後の状態を取得する。
    const { myContract, owner } = await loadFixture(deployFixture);
    await expect(myContract.connect(owner).setMaxSupply(afterUintValue)).not.to.reverted
    expect(await myContract.connect(owner).maxSupply()).to.equal(afterUintValue)
  })

payable関数実行時の送金金額を設定する

mintなどETHを受け取る関数を実行する際のテストのためには送金金額を設定する必要があります。
以下のようにテスト対象関数の最後の引数に{ value: ethers.utils.parseEther(金額) }を渡すことでmsg.valueの値を変更できます。
下記の例では1ETHを送金してmintを実行しています。

test.js
await expect(
  myContract.connect(authorizedAccount)
  .mint(quantity, { value: ethers.utils.parseEther("1") })
  ).not.to.be.reverted

ガス代を試算する

REPORT_GASという環境変数をtrueにするとテスト時のガス消費量を表示することができます。

REPORT_GAS=true npx hardhat test

サンプルの結果は以下の通りです。

·---------------------------------------------|----------------------------|-------------|---------------------------------·
|             Solc version: 0.8.4             ·  Optimizer enabled: false  ·  Runs: 200  ·  Block limit: 900000000000 gas  │
··············································|····························|·············|··································
|  Methods                                                                                                                 │
·············|································|·············|··············|·············|·················|················
|  Contract  ·  Method                        ·  Min        ·  Max         ·  Avg        ·  # calls        ·  eur (avg)    │
·············|································|·············|··············|·············|·················|················
|  TEST      ·  addWhitelists                 ·      49116  ·      260830  ·     112966  ·             14  ·            -  │
·············|································|·············|··············|·············|·················|················
|  TEST      ·  mint                          ·      68550  ·      143510  ·      90894  ·             56  ·            -  │
·············|································|·············|··············|·············|·················|················
|  Deployments                                ·                                          ·  % of limit     ·               │
··············································|·············|··············|·············|·················|················
|  TEST                                       ·          -  ·           -  ·    9684095  ·            0 %  ·            -  │
·---------------------------------------------|-------------|--------------|-------------|-----------------|---------------·

CIでテストする

Node.jsさえあればテストを実行できるため、CIでテストすることも容易です。

私はGitHub Actionsを利用して、GitHubリポジトリにpushしたら自動でテストするようにしていました。

以下のようなymlを.github/workflowsフォルダに格納しておくことで自動でテストを実行できます。

github-actions.yml
name: Node.js CI
on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [16.x]

    steps:
      - uses: actions/checkout@v3
      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v3
        with:
          node-version: ${{ matrix.node-version }}
      - run: npm ci
      - run: REPORT_GAS=true npx hardhat test

以上です。参考になれば幸いです。

Discussion