[solidity]hardhatによるコントラクトのテストTips
最近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
ネットワークの設定を明示的に入れることで増減させることができます。
module.exports = {
solidity: "0.8.4",
networks: {
hardhat: {
accounts: {
count: 100 // ←ここの数字を指定する。
}
}
}
ただし、アカウント数を多くすると実行時間がかかるので必要な時のみ増やすのが望ましいです。
逆にデフォルトの20も必要ない場合は、小さな値にすることで若干テスト時間を短縮することができます。
Fixtureを使って実行時間を短縮する
Fixtureを使用すると関数実行後の状態を保存しておいてすぐに再利用することができます。
そのため、コントラクトのデプロイやアドレスの用意はFixtureで行うことでテスト時間を短縮できます。
// 全テストで共通する初期処理を実行する。
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を実行しています。
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
フォルダに格納しておくことで自動でテストを実行できます。
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