How To: Test A Smart Contract Using The Mocha Testing Framework

In two previous blog posts, we’ve written a smart contract using the Solidity programming language and compiled the smart contract to prepare it for deployment. Now we’ll test the code using the Mocha testing framework.

If you’re unfamiliar with Mocha, take a look at the Mocha docs and/or spend some time with this tutorial.

In addition to Mocha, we’ll use Ganache and Web3. Ganache will provide us with a set of unlocked accounts we will use in our local testing environment. In this case “unlocked” means that there is no need to use a public or private key to access them. Web3 provides libraries which will allow us access to a local or remote Ethereum node. For testing purposes, we’ll be connecting to a local node, also provided by Ganache.

Let’s get started.

In the terminal and within your project, type npm install --save mocha ganache-cli web3@1.0.0-beta.28(or whichever is the latest version of web3).

Next, create a test folder, a sibling of the contracts folder. Inside of the test folder, create a file called hello.test.js.

Let’s start coding :)

First, require the Mocha assertion library, Ganache and Web3. Notice that we capitalize Web3. It is a constructor, and we’ll be using an instance of it in our code.

Require interface and the bytecode from our compile.js file that we created in the previous blog post.

Create a provider variable and pass it into our instance of Web3. This lets Web3 know where it will be getting account information.

That’s our prep code! Now we need to decide what we’ll be testing and why. As of right now, we have access to unlocked accounts and a connection to a local Ethereum node. Let’s take a look at our smart contract to decide what its behaviors should be.

The contract has two functions which we’ll want to test. Does it set an initial message? Does it set a new message?

For either of these functions to be tested, we need to deploy the contract to the local testing environment.

So in addition to testing the functions, we’ll want to make sure that the contract is deploying in the first place. To test this, we’ll check to see that it has an address.

Let’s start with a beforeEach() hook. Since we need to deploy an instance of the contract each time we run the tests, we’ll make that happen in the beforeEach().

On line 13 we get a list of all accounts. To do this, we use the eth (Ethereum) module of the Web3 library. On that module we use the getAccounts() method. Every action in Web3 is asynchronous and will return a promise. This accounts variable will return a promise that gets resolved with a list of accounts. We will be using one of those accounts to deploy our contract.

On line 14 we create an instance of our contract, passing in the parsed interface from our compile.js file. On line 15 we’re not actually deploying, despite the method’s name. What we’re doing is setting up the contract instance with the data in the form of bytecode from compile.js and with the initial string we want to use. We wrap the arguments value in brackets, because the method can take more than one argument. The send() method actually deploys the contract. We specify the address the contract is coming from. In this case we’ll use the first of the unlocked accounts provided to us by Ganache accounts[0]. And we’ll specify the amount of gas to send along to complete the deployment. Remember, we’re using fake gas in a local environment, so let’s use more than enough. 1000000 gas will do.

On lines 18 and 19 we set the provider and log the accounts.

Let’s add at least one test so that we can see the logged accounts (a beforeEach() hook will not run without a test).

Remember, we are checking to be sure the contract deploys, so here we are asserting that the contract has an address.

Before we run our test, let’s hop over to our package.json and in scripts, replace the current value of test with "mocha".

Go to your terminal at the root of your project and type npm run test.

Your test should pass and you should get a list of the ten unlocked accounts you’ll be using for the rest of your tests!

This is exciting. Let’s finish writing our tests.

We know that a contract has an address, now we’d like to test that the contract has a default message, and that the message can be changed.

has a default message test

The “default message” test is relatively simple. On line 28 we reference the hello contract. We then reference one of the methods on the hello contract, which we delved into in the previous blog post, and we use the call() method to invoke the function. We get to use the call() method for free, sans gas, because we are not updating information on the contract. We assert that the default message will be "Hey world" because that is what we set in our beforeEach() hook.

Let’s test it.

Woohoo! Two passing tests. Can we make it three?

changes the message

On line 34 we again reference the hello contract. This time we access the contract’s setMessage() method, which requires a new message to be passed in. The send() method requires an address, which tells us which account is paying the gas for this transaction. Unlike the call() method the send() method is modifying the hello contract, and will cost gas.

Let’s try it.

Yay

Our Smart Contract deploys an account, has a default message and changes the message. Tune in next time when we deploy our Smart Contract to a test network.