Now and Nawoo

NFTの制作記録、技術メモ → C#, Solidity, Blockchain, Bitcoin, Ethereum, NFT

Hardhatの使い方メモ(5) - fixture

Hardhatでテストを書くときに、fixtureというのがあります。

テストを実行すると、各テスト項目ごとに、コントラクトのデプロイなど同じ処理を繰り返し行う必要があります。 fixtureを使うと、時間のかかる処理をした後にスナップショットをとって、2回目以後の同じ処理は高速化することができます。 これによりテスト全体にかかる時間をかなり短縮できます。

とても便利な機能なのですが、Hardhatのテストでfixtureを使う方法の説明が少なくて苦労したので、ここにメモしておきます。

使い方はわかってしまえば簡単で、

  • fixture関数内にデプロイなど時間のかかる処理を書く
  • fixture関数の戻り値でテストで使用する変数を返す(signerやcontractなど)
  • 各テスト項目で loadFixture(fixture)する

というだけです。

似たようなものに beforeEachというのがありますが、こちらは単に各テスト項目ごとに繰り返す処理をまとめて書けるというだけで、特に高速化されるわけではありません。 fixtureでは、初回のloadFixtureが呼ばれたときにスナップショットを撮って、2回目以後ではスナップショットを呼び出すだけなので、処理が速くなります。

下の例ではコントラクト1つをデプロイするだけですが、複数のコントラクトを使ったり、コントラクトの関数を複数呼び出す場合では、 fixtureを使う場合と使わない場合でずいぶんとテストの実行時間に差がでます。

import { expect } from 'chai';
import { ethers, waffle } from 'hardhat';

const { loadFixture } = waffle;

describe('MyNFT', function () {
  async function fixture() {
    // fixture関数で時間のかかる処理を行う
    const [owner, user1, user2] = await ethers.getSigners();
    const factory = await ethers.getContractFactory('MyNFT');
    const contract = await factory.deploy();
    await contract.deployed();
    // signerやcontractなどテストで使用する変数を返す
    return { owner, user1, user2, contract }; 
  }

  it('テスト項目1', async function () {
    // loadFixtureでfixtureを呼び出す(初回は時間がかかる)
    const { owner, user1, user2, contract } = await loadFixture(fixture);
    await contract.mint();
    expect(...)
    ...
  });
  it('テスト項目2', async function () {
    // loadFixtureでfixtureを呼び出す(2回目以後は時間がかからない)
    const { owner, user1, user2, contract } = await loadFixture(fixture);
    await contract.connect(user1).mint();
    expect(...)
    ...
  });
  it('テスト項目3', async function () {
    const { owner, user1, user2, contract } = await loadFixture(fixture);
    await contract.connect(user2).mint();
    expect(...)
    ...
  });
});

ポイントは、

  • ethereum-waffleのloadFixtureではなく、hardhat.waffleのloadFixtureを使う
  • fixtureの引数のwalletsとproviderは使わない

です。これがわからなくて、うまく動かすのにずいぶん苦労しました。 fixture関数の型Fixture<T>では引数にsignersとproviderが指定されているのですが、使うとうまく動かないんですよね。正しい使い方はこうだよ、というのをご存じの方は教えて下さい。

また、hardhat.waffleにdeployContractがありますが、中身は単にfactory.deploy()しているだけなので、(好みの問題ですが)わざわざ使う必要はないと思います。

過去のHardhat関連の記事

Hardhatの使い方メモ (1) セットアップ~コンパイル - Now and Nawoo

Hardhatの使い方メモ (2) デプロイ~Verify - Now and Nawoo

Hardhatの使い方メモ(3) scripts - Now and Nawoo

Hardhatの使い方メモ(4) テスト - Now and Nawoo ←基本のテストの書き方はこちら