or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

contract-interaction.mdcore-deployment.mddeployment-management.mddiamond-deployment.mdindex.mdnamed-accounts.mdproxy-deployment.mdtest-fixtures.mdverification.md
tile.json

test-fixtures.mddocs/

Test Fixtures

Fast test execution using EVM snapshots with automatic deployment state management and custom fixture creation.

Capabilities

Test Fixtures

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
    });
  });
});

Custom Fixtures

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"));
  });
});

Fixture Integration with Deploy Scripts

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"]);
  });
});

Fixture Performance Optimization

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
    });
  });
});

Types

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[];