Skip to content

defi-wonderland/smock-foundry

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Version License: MIT

Smock Foundry

A plugin for foundry that automatically generates Solidity mocks for every contract in your project.

Features

  • Get rid of your folder of "mock" contracts and just use foundry.
  • Keep your tests simple with straightforward mock functions.
  • Mock up external calls and internal variables in a beautiful and orderly fashion.

Installation

You can install the plugin via yarn:

yarn add @defi-wonderland/smock-foundry --save-dev

Basic Usage

Creating mocks

To generate the mock contracts all you have to do is run:

yarn smock-foundry --contracts solidity/contracts

The smock-foundry command accepts the following options:

Option Default Notes
contracts — The path to the solidity contracts to mock
root . The path to the root of the project
mocks ./test/smock The path to the generated mock contracts
ignore [] A list of directories to ignore, e.g. --ignore libraries

Be sure to gitignore the generated smock directory.

Using mocks

Let's say you have a Greeter contract in your project at contracts/Greeter.sol:

contract Greeter {
  string internal _greeting;

  constructor(string memory greeting) {
    _greeting = greeting;
  }

  function greet() public view returns (string memory) {
    return _greeting;
  }
}

After running the generator, you will have a mock contract located at ${mocks}/contracts/MockGreeter.sol:

contract MockGreeter is Greeter {
  function mock_call_greet(string memory __greeting) external {
    // Mocks the greet() function calls
  }

  function set__greeting(string memory greeting) public {
    // Sets the value of `greeting`
  }
}

The next step would be importing the mock contract in your unit tests, deploying it and allowing it to use the cheatcodes, specifically vm.mockCall.

import 'forge-std/Test.sol';

import { MockGreeter } from '/path/to/smock/contracts/MockGreeter.sol';
import { SmockHelper } from '/path/to/smock/SmockHelper.sol';

contract BaseTest is Test, SmockHelper {
  MockGreeter public greeter;

  function setUp() public {
    // The `deployMock` call is equivalent to
    // address _greeterAddress = address(new MockGreeter('Hello'));
    // vm.label(_greeterAddress, 'Greeter');
    // vm.allowCheatcodes(_greeterAddress);
    // return _greeterAddress;

    greeter = MockGreeter(
      deployMock('Greeter', type(MockGreeter).creationCode, abi.encode('Hello'))
    );
  }
}

Then enjoy the wonders of mocking:

// Mock the `greet` function to return 'Hola' instead of 'Hello'
greeter.mock_call_greet('Hola');

// Or you can achieve the same by setting the internal variable
greeter.set__greeting('Hola');

Gotchas

  • Please, note that if you want to mock internal functions, you must make them virtual. The tool will not generate mocks for internal functions that are not virtual.
  • Cannot set private variables and mock private functions.
  • Mocking of structs containing mappings is not supported.
  • Mocking of multi-dimensional arrays of structs is not supported.

Licensing

The primary license for Smock Foundry is MIT, see LICENSE.

Contributors

Maintained with love by Wonderland. Made possible by viewers like you.