Migrate Mocha tests from Hardhat 2
This guide walks you through the changes needed to migrate your Mocha tests from Hardhat 2 to Hardhat 3.
Hardhat 3 is ESM-first, so JavaScript test files (.js) are treated as ES modules. See the ESM migration guide for the conversion steps, or for how to keep your tests as CommonJS with a .cjs extension.
If you keep your tests as CommonJS, you can’t require("hardhat") at the top level. Use a dynamic import from a before hook instead:
describe("suite", function () { let hre; before(async function () { ({ default: hre } = await import("hardhat")); });
it("some test", async function () { // use the hre });});Network connections
Section titled “Network connections”One of the biggest changes in Hardhat 3 is how network connections work. You don’t have a single, global network connection anymore. Instead, you create and manage network connections explicitly. For example, if you have a test that uses the JSON-RPC provider:
describe("suite", function () { it("some test", async function () { const blockNumber = await hre.network.provider.send("eth_blockNumber"); });});You now have to create a network connection first:
describe("suite", function () { it("some test", async function () { const { provider } = await hre.network.create(); const blockNumber = await provider.send("eth_blockNumber"); });});Most of the time, you’ll want to use the same provider for multiple tests. If you’re using ESM syntax, you can use top-level await and define it as a shared variable:
const { provider } = await hre.network.create();
describe("suite", function () { it("some test", async function () { const blockNumber = await provider.send("eth_blockNumber"); });});If you’re using CommonJS modules instead, you can define the provider in a before block:
let provider;
describe("suite", function () { before(async function () { ({ provider } = await hre.network.create()); });
it("some test", async function () { const blockNumber = await provider.send("eth_blockNumber"); });});The ethers.js object
Section titled “The ethers.js object”In Hardhat 2, the hardhat-ethers plugin adds an ethers object to the Hardhat Runtime Environment, meaning you can do things like this in a test:
const hre = require("hardhat");
describe("suite", function () { it("some test", async function () { const contract = await hre.ethers.deployContract("MyContract"); });});In Hardhat 3, this same ethers object is included as part of each created network connection:
import hre from "hardhat";
describe("suite", function () { let ethers; before(async function () { ({ ethers } = await hre.network.create()); });
it("some test", async function () { const contract = await ethers.deployContract("MyContract"); });});Chai matchers changes
Section titled “Chai matchers changes”Some of the Chai matchers have changed in Hardhat 3, to make them work with multiple network connections.
These changes are:
- Some matchers take an instance of
HardhatEthers(i.e. theethersobject from the network connection), as shown in this list. .revertedis now.revert(ethers).revertedWithoutReason()is now.revertedWithoutReason(ethers)changeEtherBalance,changeEtherBalances,changeTokenBalance, andchangeTokenBalancesnow take an instance ofHardhatEthersas the first argument.
Hardhat network helper changes
Section titled “Hardhat network helper changes”The @nomicfoundation/hardhat-network-helpers package is now a plugin. Instead of importing its functions directly, now you get the helpers as part of the network connection:
import { network } from "hardhat";const { networkHelpers } = await network.create();
await networkHelpers.mine(5);