Fast test execution using EVM snapshots with automatic deployment state management and custom fixture creation.
Execute deployments as test fixtures with automatic EVM snapshot management.
/**
* Execute deployments as test fixture with EVM snapshots
* @param tags - Deployment tags to execute (optional)
* @param options - Fixture configuration options
* @returns Promise resolving to deployment results
*/
fixture(
tags?: string | string[],
options?: {
fallbackToGlobal?: boolean;
keepExistingDeployments?: boolean;
}
): Promise<{ [name: string]: Deployment }>;Usage Examples:
import { deployments, getNamedAccounts } from "hardhat";
describe("Contract Tests", function () {
// Run all deployments before each test
beforeEach(async function () {
await deployments.fixture();
});
it("should have deployed contracts", async function () {
const contract = await deployments.get("MyContract");
expect(contract.address).to.not.be.undefined;
});
});
// Use specific deployment tags
describe("Token Tests", function () {
beforeEach(async function () {
await deployments.fixture(["Token", "TokenSale"]);
});
it("should work with tokens", async function () {
const token = await deployments.get("Token");
const sale = await deployments.get("TokenSale");
// Test logic here
});
});
// Multiple tags
describe("Full System Tests", function () {
beforeEach(async function () {
await deployments.fixture(["Infrastructure", "Business", "Frontend"]);
});
});
// Fixture with options
describe("Persistent Tests", function () {
beforeEach(async function () {
await deployments.fixture(["Core"], {
keepExistingDeployments: true, // Don't reset existing deployments
fallbackToGlobal: true, // Use global deployments if tag not found
});
});
});Create custom test fixtures with automatic snapshot optimization.
/**
* Create a custom fixture function with EVM snapshot optimization
* @param func - Fixture function to execute
* @param id - Optional unique identifier for the fixture
* @returns Function that executes fixture with snapshot caching
*/
createFixture<T, O>(
func: FixtureFunc<T, O>,
id?: string
): (options?: O) => Promise<T>;
type FixtureFunc<T, O> = (
env: HardhatRuntimeEnvironment,
options?: O
) => Promise<T>;Usage Examples:
import { deployments } from "hardhat";
import { ethers } from "hardhat";
// Custom fixture for complex test setup
const setupTokenEcosystem = deployments.createFixture(
async ({ deployments, getNamedAccounts }, options) => {
await deployments.fixture(["Token", "DEX", "Governance"]);
const { deployer, user1, user2 } = await getNamedAccounts();
const token = await ethers.getContract("Token", deployer);
const dex = await ethers.getContract("DEX", deployer);
// Setup initial state
await token.mint(user1, ethers.utils.parseEther("1000"));
await token.mint(user2, ethers.utils.parseEther("1000"));
return {
token,
dex,
deployer,
user1,
user2,
initialBalance: ethers.utils.parseEther("1000"),
};
},
"tokenEcosystem" // Optional ID for caching
);
describe("DEX Tests", function () {
it("should handle token swaps", async function () {
const { token, dex, user1, user2 } = await setupTokenEcosystem();
// Test logic with pre-configured state
await token.connect(user1).approve(dex.address, ethers.utils.parseEther("100"));
await dex.connect(user1).swap(ethers.utils.parseEther("100"));
});
it("should handle liquidity provision", async function () {
const { token, dex, user2, initialBalance } = await setupTokenEcosystem();
// Each test gets the same pre-configured state via snapshots
await token.connect(user2).approve(dex.address, initialBalance);
await dex.connect(user2).addLiquidity(initialBalance);
});
});
// Fixture with options
const setupWithCustomAmounts = deployments.createFixture(
async ({ deployments, getNamedAccounts }, options) => {
await deployments.fixture(["Token"]);
const { deployer, user1 } = await getNamedAccounts();
const token = await ethers.getContract("Token", deployer);
const amount = options?.amount || ethers.utils.parseEther("100");
await token.mint(user1, amount);
return { token, user1, amount };
}
);
describe("Custom Amount Tests", function () {
it("should work with default amount", async function () {
const { token, user1, amount } = await setupWithCustomAmounts();
expect(amount).to.equal(ethers.utils.parseEther("100"));
});
it("should work with custom amount", async function () {
const { token, user1, amount } = await setupWithCustomAmounts({
amount: ethers.utils.parseEther("500"),
});
expect(amount).to.equal(ethers.utils.parseEther("500"));
});
});Use fixtures that automatically execute tagged deploy scripts.
Usage Examples:
// Deploy script with tags (deploy/001_token.ts)
import { HardhatRuntimeEnvironment } from "hardhat/types";
import { DeployFunction } from "hardhat-deploy/types";
const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const { deployments, getNamedAccounts } = hre;
const { deploy } = deployments;
const { deployer } = await getNamedAccounts();
await deploy("Token", {
from: deployer,
args: ["MyToken", "MTK", 18],
log: true,
});
};
func.tags = ["Token", "ERC20"];
func.dependencies = ["Infrastructure"];
export default func;
// Test using deploy script tags
describe("Tagged Deployment Tests", function () {
beforeEach(async function () {
// This will execute deploy scripts with "Token" tag
await deployments.fixture(["Token"]);
});
it("should have token deployed", async function () {
const token = await deployments.get("Token");
expect(token.address).to.not.be.undefined;
});
});
// Test with dependency resolution
describe("Dependency Tests", function () {
beforeEach(async function () {
// This will execute "Infrastructure" dependencies first, then "Token"
await deployments.fixture(["Token"]);
});
});
// Test subset of deployments
describe("ERC20 Tests", function () {
beforeEach(async function () {
// Execute all scripts tagged with "ERC20"
await deployments.fixture(["ERC20"]);
});
});Fixtures automatically use EVM snapshots for performance optimization.
Usage Examples:
// Expensive setup fixture
const expensiveSetup = deployments.createFixture(
async ({ deployments, getNamedAccounts }) => {
// This expensive setup only runs once, then snapshots are used
await deployments.fixture(["ComplexSystem"]);
const { deployer } = await getNamedAccounts();
// Expensive initialization
const contracts = await Promise.all([
ethers.getContract("ContractA", deployer),
ethers.getContract("ContractB", deployer),
ethers.getContract("ContractC", deployer),
]);
// Complex state setup
for (let i = 0; i < 100; i++) {
await contracts[0].createItem(i);
}
return { contracts, deployer };
},
"expensiveSetup"
);
describe("Performance Tests", function () {
// Each test gets the expensive setup instantly via snapshots
it("test 1", async function () {
const { contracts } = await expensiveSetup();
// Test logic
});
it("test 2", async function () {
const { contracts } = await expensiveSetup();
// Test logic - same setup, no re-execution
});
it("test 3", async function () {
const { contracts } = await expensiveSetup();
// Test logic - still using cached snapshot
});
});
// Nested describe blocks with different fixtures
describe("System Tests", function () {
describe("Basic functionality", function () {
beforeEach(async function () {
await deployments.fixture(["Basic"]);
});
it("basic test", async function () {
// Test logic
});
});
describe("Advanced functionality", function () {
beforeEach(async function () {
await deployments.fixture(["Basic", "Advanced"]);
});
it("advanced test", async function () {
// Test logic
});
});
});interface FixtureOptions {
/** Use global deployments if tagged deployments not found */
fallbackToGlobal?: boolean;
/** Keep existing deployments instead of resetting */
keepExistingDeployments?: boolean;
}
type FixtureFunc<T, O> = (
env: HardhatRuntimeEnvironment,
options?: O
) => Promise<T>;
interface Deployment {
address: Address;
abi: ABI;
receipt?: Receipt;
transactionHash?: string;
args?: any[];
linkedData?: any;
implementation?: string;
facets?: Facet[];
libraries?: Libraries;
metadata?: string;
bytecode?: string;
deployedBytecode?: string;
history?: Deployment[];
numDeployments?: number;
}
type Address = string;
type ABI = any[];