or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

chai-matchers.mdcompiler-utilities.mdcontract-deployment.mdens-utilities.mdindex.mdmock-contracts.mdoptimism-provider.mdprovider-testing.md
tile.json

mock-contracts.mddocs/

Mock Contracts

Advanced mocking system for creating configurable smart contract mocks that can return specific values, revert with custom reasons, track call history, and enable comprehensive testing of contract interactions without deploying actual contracts.

Capabilities

Deploy Mock Contract

Create fully configurable mock contracts from ABI definitions, enabling complete control over function behavior for testing complex contract interactions.

/**
 * Deploy a mock contract that can be configured to return specific values
 * @param signer - Signer to deploy the mock contract with
 * @param abi - Contract ABI to mock
 * @param options - Optional deployment configuration
 * @returns Promise resolving to MockContract instance
 */
function deployMockContract<T extends BaseContract = BaseContract>(
  signer: Signer,
  abi: ABI,
  options?: DeployOptions
): Promise<MockContract<T>>;

/**
 * ABI format specification for mock contracts
 */
type ABI = string | Array<utils.Fragment | JsonFragment | string>;

/**
 * Options for mock contract deployment
 */
type DeployOptions = {
  /** Specific address to deploy mock at */
  address: string;
  /** Whether to override existing contract at address */
  override?: boolean;
};

Usage Examples:

import { deployMockContract } from "ethereum-waffle";

// Deploy mock from ABI
const mockERC20 = await deployMockContract(wallet, ERC20.abi);

// Deploy at specific address
const mockContract = await deployMockContract(wallet, contractAbi, {
  address: "0x1234567890123456789012345678901234567890",
  override: true
});

// Configure mock behavior
await mockERC20.mock.balanceOf.returns(1000);
await mockERC20.mock.transfer.returns(true);

// Use in tests
const balance = await mockERC20.balanceOf(wallet.address);
console.log(balance); // 1000

MockContract Interface

Enhanced contract interface that extends regular Contract with comprehensive mocking capabilities for all contract functions.

/**
 * Enhanced contract with mocking capabilities
 */
interface MockContract<T extends BaseContract = BaseContract> extends Contract {
  /** Mock configuration for all contract functions */
  mock: {
    [key in ((keyof T['functions'] | 'receive'))]: StubInterface;
  };
  
  /** 
   * Call function on another contract through this mock
   * @param contract - Target contract to call
   * @param functionName - Function name to call
   * @param params - Function parameters
   * @returns Promise resolving to call result
   */
  call(contract: Contract, functionName: string, ...params: any[]): Promise<any>;
  
  /**
   * Static call function on another contract through this mock
   * @param contract - Target contract to staticcall
   * @param functionName - Function name to call
   * @param params - Function parameters  
   * @returns Promise resolving to staticcall result
   */
  staticcall(contract: Contract, functionName: string, ...params: any[]): Promise<any>;
}

Usage Examples:

import { deployMockContract } from "ethereum-waffle";

const mockToken = await deployMockContract(wallet, ERC20.abi);

// Configure multiple functions
await mockToken.mock.name.returns("Mock Token");
await mockToken.mock.symbol.returns("MOCK");
await mockToken.mock.decimals.returns(18);
await mockToken.mock.totalSupply.returns(1000000);

// Use call method to proxy calls
const result = await mockToken.call(realContract, "complexFunction", param1, param2);

// Use staticcall for view functions
const viewResult = await mockToken.staticcall(realContract, "viewFunction", param);

Stub Interface

Fluent interface for configuring mock function behavior with support for return values, reverts, argument matching, and call queuing.

/**
 * Interface for configuring mock function behavior
 */
interface StubInterface {
  /**
   * Configure function to return specific values
   * @param args - Values to return (matching function output types)
   * @returns StubInterface for chaining
   */
  returns(...args: any): StubInterface;
  
  /**
   * Configure function to revert with generic message
   * @returns StubInterface for chaining
   */
  reverts(): StubInterface;
  
  /**
   * Configure function to revert with specific reason
   * @param reason - Custom revert reason string
   * @returns StubInterface for chaining
   */
  revertsWithReason(reason: string): StubInterface;
  
  /**
   * Configure function behavior for specific arguments
   * @param args - Arguments to match for this configuration
   * @returns StubInterface for chaining
   */
  withArgs(...args: any[]): StubInterface;
}

Usage Examples:

import { deployMockContract } from "ethereum-waffle";

const mockContract = await deployMockContract(wallet, contractAbi);

// Basic return configuration
await mockContract.mock.getValue.returns(42);

// Revert configuration
await mockContract.mock.restrictedFunction.reverts();
await mockContract.mock.validateInput.revertsWithReason("Invalid input");

// Argument-specific behavior
await mockContract.mock.transfer
  .withArgs(wallet.address, 100)
  .returns(true);

await mockContract.mock.transfer
  .withArgs(wallet.address, 0)
  .revertsWithReason("Amount must be greater than 0");

// Chained configuration for multiple calls
await mockContract.mock.getBalance
  .returns(1000)  // First call returns 1000
  .returns(900)   // Second call returns 900  
  .returns(800);  // Third call returns 800

// Mixed behavior
await mockContract.mock.complexFunction
  .withArgs(1)
  .returns("success")
  .withArgs(2)
  .reverts()
  .withArgs(3)
  .revertsWithReason("Invalid parameter");

Advanced Mock Patterns

Complex mocking scenarios including receive function handling, call queuing, and state-dependent behavior.

Receive Function Mocking:

// Configure receive function behavior
await mockContract.mock.receive.reverts();
await mockContract.mock.receive.revertsWithReason("Contract does not accept ETH");

// Note: receive function cannot be configured to return values

Call Queue Management:

// Set up call queue - first call uses mockReturns, subsequent use queueReturn
await mockContract.mock.getPrice
  .returns(100)  // First call
  .returns(105)  // Second call  
  .returns(110)  // Third call
  .reverts();    // Fourth call reverts

// Test sequential calls
console.log(await mockContract.getPrice()); // 100
console.log(await mockContract.getPrice()); // 105
console.log(await mockContract.getPrice()); // 110
// Next call will revert

State-Dependent Mocking:

// Configure different behavior based on arguments
const mockOracle = await deployMockContract(wallet, oracleAbi);

await mockOracle.mock.getPrice
  .withArgs("ETH")
  .returns(ethers.utils.parseEther("2000"));

await mockOracle.mock.getPrice  
  .withArgs("BTC")
  .returns(ethers.utils.parseEther("40000"));

await mockOracle.mock.getPrice
  .withArgs("UNKNOWN")
  .revertsWithReason("Price not available");

// Test with different arguments
const ethPrice = await mockOracle.getPrice("ETH");   // 2000 ETH
const btcPrice = await mockOracle.getPrice("BTC");   // 40000 ETH  
// await mockOracle.getPrice("UNKNOWN"); // Reverts

Integration Testing:

// Use mocks in complex integration tests
const mockToken = await deployMockContract(wallet, ERC20.abi);
const realDEX = await deployContract(wallet, DEXJson, [mockToken.address]);

// Configure token behavior for DEX testing
await mockToken.mock.transferFrom.returns(true);
await mockToken.mock.balanceOf.returns(1000);

// Test DEX functionality with mocked token
await realDEX.swapTokens(100);

// Verify interactions using chai matchers
expect(mockToken.transferFrom).to.have.been.calledWith(wallet.address, realDEX.address, 100);