If you want to test a contract that depends on other contract, but you don't own the latter contract (or you do but you want to test the first contract in isolation), you need to either stub or mock that dependency.
If you are not sure about the difference between a stub and a mock, check this StackOverflow question
If the contract you are testing receives its dependency in some way, you can write a stub contract and use that in your tests. It would be something like this:
contract ContractToTest {
Dependency dependency;
constructor (Dependency _dependency) {
dependency = _dependency;
}
function functionToTest() public {
// uses `dependency.f` somehow
}
}
contract StubDependency {
uint fakeValue;
constructor (uint _fakeValue) {
fakeValue = _fakeValue;
}
function f() public returns (uint) {
return fakeValue;
}
}
And then in your tests you would deploy StubDependency
and use that address when you deploy your instance of ContractToTest
There are two libraries you can use to mock a contract.
The first one is Waffle. To use this, you should install and use hardhat-waffle and then make sure that you use deployMockContract
from the waffle instance in the Hardhat Runtime Environment:
const { waffle } = require("hardhat")
const { deployMockContract } = waffle
Don't import deployMockContract
from @ethereum-waffle/mock-contract
as the Waffle's docs do.
The second library you can use is smock. Check its docs to learn how to use it.
This is an advanced method and you should only use it if none of the previous options work for you. The hardhat_setCode
RPC method lets you change the bytecode of an address while preserving its storage. So you could write a stub contract, get its bytecode (for example, by just deploying it and getting the resulting code, or using the compilation output), and then replace the contract your contract depends on with the stubbed bytecode. This could be useful if you are using mainnet forking
and use some existing contract.