4

In my Test I want to mock an external function that I call inside my smart contract to test both conditions without writing and deploying an extra fake smart contract.

My Contract

//SPDX-License-Identifier: Unlicense
pragma solidity 0.8.4;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
 
contract Greeter {
    uint256 private number = 0;
    constructor() {}
    function getNumber() public view returns (uint256) {
        return number;
    }
    // if transferFrom returns true then set number to 1 , else set number to 2
    function setNumber(IERC20 erc20Address) public {
        erc20Address.transferFrom(msg.sender, address(this), 10**18) ? number = 1 : number = 2;
    }
}

The external contract

//SPDX-License-Identifier: Unlicense
pragma solidity 0.8.4;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract MyERC20 is ERC20 {
    constructor() ERC20("Name", "M-T") {}
}

The function to mock is transferFrom

2 Answers 2

4

make sure you have installed the extra packages npm install --save-dev @nomiclabs/hardhat-waffle ethereum-waffle chai @nomiclabs/hardhat-ethers ethers and then use Waffle for that.

waffle provides a deployMockContract method to create an instance of the contract that can be mocked, and then its functions can be mocked with <mocked-instance>.mock.<function-name>.returns(<values..>)

https://ethereum-waffle.readthedocs.io/en/latest/mock-contract.html#mock-contract

For my 2 smart contract the test will look like this:


const { expect } = require("chai");
const { waffle, ethers } = require('hardhat');
const { deployMockContract, provider } = waffle;


describe("Test", function () {

  it('should test both conditions of the setNumber function', async () => {
    const [deployerOfContract] = provider.getWallets();
    // deploy the contract to Mock
    const MyERC20 = require('../artifacts/contracts/MyERC20.sol/MyERC20.json');
    const mockedMyERC20 = await deployMockContract(deployerOfContract, MyERC20.abi);

    // deploy the Greeter contract
    const Greeter = await ethers.getContractFactory('Greeter');
    const greeterInstance = await Greeter.deploy();

    // mock the function transferFrom -> return always true
    await mockedMyERC20.mock.transferFrom.returns(true);
    // call setNumber
    await greeterInstance.setNumber(mockedMyERC20.address);

    let storedNumber = await greeterInstance.getNumber();
    expect(storedNumber.toString()).to.equal('1');

    // mock the function transferFrom -> return always false
    await mockedMyERC20.mock.transferFrom.returns(false);
    // call setNumber
    await greeterInstance.setNumber(mockedMyERC20.address);
    storedNumber = await greeterInstance.getNumber();
    expect(storedNumber.toString()).to.equal('2');
  });

});

and then run

  • npx hardhat compile
  • npx hardhat test

result

  Test
    ✔ should test both conditions of the setNumber function (858ms)
  1 passing (1s)
0

Try out the smock hardhat contracts mocking library.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.