How to use fixtures in Hardhat unit tests

In this tutorial, we are going to learn how to use fixtures in Hardhat unit tests to set up your tests without repeating code and to improve the tests speed.

In this tutorial, we are going to learn how to use fixtures in Hardhat unit tests to set up your tests without repeating code and to improve the tests speed.

Fixtures allow you to run code before each test set them up. The most common use case is to use fixtures to deploy the smart contracts you test and get them to the desired state.

In a normal Mocha test (the library used by Hardhat for the unit tests), you would use a beforeEach function to run code before each test. But it makes the tests slower.

A fixture is much faster than a beforeEach because the first time you use the it's normally executed but the state of the smart contract and the network are saved.

That means the next times you use it, instead of executing the fixture again, it will just load and use the state of the network and the smart contract that was saved when it ran for the first time.

With fixtures, anything you do in your test will be undone before running the next test.

To use fixtures, your first need to install extra libraries in your project to include waffle which is the testing library that includes the fixtures.

Here is the documentation on how to do that: https://hardhat.org/hardhat-runner/plugins/nomiclabs-hardhat-waffle#environment-extensions

Once you've installed the libraries and updated your Hardhat config, you can use fictures.

To use fixtures in Hardhat, we use the loadFixture function and you pass the function that you want to run before the tests in the parameters of loadFixture:

const { expect } = require("chai");
const { ethers } = require("hardhat");
const { waffle } = require('hardhat');
const { loadFixture } = waffle;

describe("MyContract", function () {
  async function deployAndSetStateFixture() {
    const MyContract = await ethers.getContractFactory("MyContract");
    const myContract = await MyContract.deploy();
    
    await myContract.setMyProperty(3)

    return { myContract };
  }

  it("The right value is set on myProperty", async function () {
    const { myContract } = await loadFixture(deployAndSetStateFixture);
    expect(await myContract.myProperty()).to.equal(3);
  });
});

As you can see, you have to import waffle from hardhat and then import loadFixture from waffle. Then, all you have to do is define a function that will be executed before the tests and that will be your fixture.

To use that fixture in your test, you do: await loadFixture(myFunction)

Unfortunately, you cannot pass arguments to the function that you call inside the fixture so you have to create multiple functions that will do the same thing every time and take nothing in parameters.

The values you return in the fixture function will be returned by loadFixture when you call it in a unit test.

And lastly, loadFixture passes 2 parameters to your function:

  • The first one is an array of accounts that you can use for transactions, the first one in that array is used by default
  • The second parameter is the provider and it's an object that allows you to interact with the blockchain and the wallets

I'll let you have a look at the documentation of Waffle to have get information about it: https://ethereum-waffle.readthedocs.io/en/latest/

And that's it 🎉

Thank you for reading this article