The Highly Optimistic Dev Blog #03: Introducing Smock v2

Author: Kelvin Fichter

Solidity devs: meet Smock v2. The Solidity mocking library. A collaboration between Optimism and the fantastic team over at DeFi Wonderland.

Smart contract testing has historically been… hard? If not hard, then just confusing. Way back in the early days of Solidity the best way to test a contract was to write another contract responsible for doing all the testing. This was a terrible idea for about 20 different reasons. I’ll name a few of the most important ones:

  1. You had to write your test code in Solidity.

It was a huge time sink for everyone involved. It was mostly okay because smart contracts were relatively simple back then. But, of course, this lack of testing infrastructure meant that contracts couldn’t be very complex.

It took a while, but we finally evolved to use JavaScript testing frameworks like Truffle to vastly improve the testing experience. We got to inherit some of the nice features of tools like chai and mocha. Our tests became at least somewhat legible. You could actually build contract systems with a reasonable amount of complexity.

Hardhat eventually came along and improved upon many of the things that Truffle, admittedly, did first. But Hardhat’s major advancement was its plugin system — developers now had the ability easily to manipulate their testing environment to an extent never really possible with Truffle.

Yet throughout all of this improvement, Solidity developers have still had to deal with one absolutely terrible pattern: mock contracts, written in Solidity, just to be able to unit test very specific functionality. I mean, really??? Here are a few reasons why this is so bad:

  1. You have to write your test code in Solidity.

???

Yeah. Anyway. We fixed that.

Introducing: Smock v2. Contract mocking in JavaScript. More powerful than you can imagine. Never write a mock contract in Solidity again.

Features

Fake any contract

const myFake = await smock.fake('MyContract');
const myOtherFake = await smock.fake(myContractFactory);
const myOtherOtherFake = await smock.fake(myContractABI);
const myOtherOtherOtherFake = await smock.fake(myContractInstance);

Manipulate any contract function

myFake.myFunction.returns(1234);
myFake.myOtherFunction.returns({ myStructField: 1234 });
myFake.myOtherOtherFunction.returns((fnArg) => { return fnArg * 10 });
myFake.myOtherOtherOtherFunction.reverts(); // womp womp!

Make assertions about calls

expect(myFake.myFunction).to.have.been.calledOnce;
expect(myFake.myFunction).to.have.been.calledWith(1234);
expect(myFake.myFunction).to.have.been.calledBefore(myFake.myOtherFunction);

Create mocks backed by real contracts

const myMockFactory = await smock.mock('MyContract');
const myMock = await myMockFactory.deploy();
// Does everything a fake can do!
myMock.myFunction.returns(5678);

Manipulate variables inside of mocks

await myMock.setVariable('myVariable', 1234);
await myMock.setVariable('myOtherVariable', { myStructValue: 1234 });

And so much more…

Do I really need to say anything else? Go try it out. It’ll change your life. Seriously.

https://github.com/defi-wonderland/smock

Scaling Ethereum