Testing with Hardhat

In Hardhat 3, network helpers and ethers are obtained from a network connection: const { ethers, networkHelpers } = await network.connect();, and tests can also be written in Solidity (Foundry-compatible style) and run with npx hardhat test solidity.

35.1 TypeScript tests with ethers.js (Hardhat + Mocha/Chai)

In Hardhat 3, network helpers and ethers are obtained from a network connection: const { ethers, networkHelpers } = await network.connect();, and tests can also be written in Solidity (Foundry-compatible style) and run with npx hardhat test solidity. Hardhat 3 also offers a viem-based toolbox with node:test.

// test/SafeBank.ts — Hardhat 2 style with @nomicfoundation/hardhat-toolbox
import { expect } from "chai";
import { ethers } from "hardhat";
import { loadFixture, time } from "@nomicfoundation/hardhat-toolbox/network-helpers";

describe("SafeBank", function () {
  async function deployFixture() {
    const [owner, alice, bob] = await ethers.getSigners();
    const bank = await ethers.deployContract("SafeBank");
    await bank.waitForDeployment();
    return { bank, owner, alice, bob };
  }

  it("accepts deposits", async function () {
    const { bank, alice } = await loadFixture(deployFixture);
    await bank.connect(alice).deposit({ value: ethers.parseEther("1") });
    expect(await bank.balances(alice.address)).to.equal(ethers.parseEther("1"));
  });

  it("withdraws and changes balances", async function () {
    const { bank, alice } = await loadFixture(deployFixture);
    await bank.connect(alice).deposit({ value: ethers.parseEther("2") });
    await expect(bank.connect(alice).withdraw()).to.changeEtherBalances(
      [alice, bank],
      [ethers.parseEther("2"), ethers.parseEther("-2")]
    );
  });

  it("reverts without balance", async function () {
    const { bank, bob } 
…

35.2 viem-based tests

import { network } from "hardhat";
import { parseEther } from "viem";
import { describe, it } from "node:test";
import assert from "node:assert/strict";

describe("SafeBank (viem)", async function () {
  const { viem } = await network.connect();

  it("deposits", async function () {
    const bank = await viem.deployContract("SafeBank");
    const [alice] = await viem.getWalletClients();
    await bank.write.deposit({ value: parseEther("1"), account: alice.account });
    assert.equal(await bank.read.balances([alice.account.address]), parseEther("1"));
  });
});

35.3 Hardhat features

console.log in Solidity import "hardhat/console.sol"; (remove before deployment)

forking: { url, blockNumber } in network config hardhat-gas-reporter / built-in gas statistics

solidity-coverage (Hardhat 2), npx hardhat test --coverage (Hardhat 3) Hardhat Ignition modules hardhat-verify (npx hardhat verify) Solidity stack traces on failed txs

35.4 Foundry vs Hardhat

TypeScript/JS (and Solidity in v3) fast (v3 Rust runtime) Fuzz / invariant

Solidity tests in v3; otherwise via plugins/Echidna CLI-centric, git submodules or soldeer npm, rich plugin ecosystem, frontend-friendly protocol/security work full-stack dApps; many teams use both