Adrian Li

@adrianmcli

Minimal Solidity contract testing with Ganache and Jest

Recently, while writing some frontend tooling for dapps (stay tuned), I encountered the need to quickly test that my library would work with real Solidity contracts.

While I always recommend dapp makers to start with a Truffle project, sometimes you need something a little more lightweight. For example, instead of having to run migrations, maybe you just want to compile a smart contract and test right-away.

No worries, the Truffle suite of tools has got you covered!

In this tutorial, I will show you how to setup minimal smart contract testing with Ganache and Jest.

If you don’t need a step-by-step explanation, the example repo is right here:

Just three steps

In essence, there are only three things you need to do:

  1. Compile the Solidity contract
  2. Spawn a test “blockchain”
  3. Deploy the contract.

Sounds daunting right? But thanks to Truffle and the community at large, this is actually all a lot simpler than you might think.

Our Contract

Before we get started, let’s create our contract, SimpleStorage.sol:

This contract is very simple, it allows you to get and set an integer, that’s it.

Compiling the contract

We’re going to be using solc-js to compile our Solidity contract. This means we will have to conform to their way of doing things and this might get a little funky so let’s create a new file for that.

Start a new file called compile.js and paste in the following:

We are exporting a function compile that will take in a filename and look for it in the same folder. We pass solc the options object (i.e. input) in the way that it wants and it spits out a JSON that we can extract our artifact from.

Do note that I also have a version of the repo using truffle-compile instead of solc. There are pros and cons to either one, feel free to make your own judgement call.

Spawning a test blockchain with Ganache

For this part and the next, I’ll be explaining the concepts first and then I’ll show you the whole file where it all comes together. So please pay attention, I promise it’ll pay off!

Spawning a test blockchain is way simpler than I could have ever hoped for. Literally the only thing you need to do is:

// import Ganache
const Ganache = require("ganache-core");
// spawn the test "blockchain" provider
const provider = Ganache.provider();
// use it like how you would normally use a provider
const web3 = new Web3(provider);
const accounts = await web3.eth.getAccounts();

The one-liner Ganache.provider() essentially does all the heavy lifting for you. It basically spawns a blockchain in-memory and gives you the provider object to interact with it.

From the provider, we can easily get theweb3 instance and subsequently the accounts array that we will need in the next step.

Deploy your contract

Now that we have web3 as well as the contract artifact, we can deploy it onto our test blockchain.

const instance = new web3.eth.Contract(SimpleStorage.abi);
const deployedInstance = await instance.deploy({
data: SimpleStorage.evm.bytecode.object
}).send({
from: accounts[0],
gas: 150000
});

This is standard usage of the Web3.js API (v1.0). We first create our contract instance by passing in the ABI from our artifact SimpleStorage.abi. And then we deploy it by calling .deploy() with the bytecode from our contract artifact, and finally send() from our account with a specified gas amount to make sure it goes through.

Putting it all together

Now that we understand what we need to do, we can make a new file called test.js and start writing our test. Most of what we talked about is setting up the environment for our tests, so it will go under the beforeAll() hook from Jest.

At this point, I am going to show you the entirety of this file:

Note that the provider, web3, accounts, andcontractInstance variables are declared outside of the beforeAll() hook so that we can use those them in our tests.

We also have an afterAll() hook where we call stop() on the provider to prevent memory leaks.

Finally, the actual example test demonstrates setting and getting a value.

Some thoughts

Of course, this might get a little un-wieldy if you have many different contracts and they inherit from each other. For those use-cases, I would highly recommend a traditional Truffle project.

However, if you have something simple to test, or if you want to test out your frontend separately from your Truffle project, this is a great option that allows you to do so.

Do keep in mind that spawning a Ganache provider does take a couple seconds, so try not to do it all-over the place. If you start having to do that, it may be another sign that you should consider converting your project to a full-on Truffle project instead.

This format may not be for everyone, so do check out the example repo that you can clone and run for yourself!

If you liked this article, please give me a few claps!

More by Adrian Li

Topics of interest

More Related Stories