The shortest guide to testing Ethereum smart contracts

Source code is on Github

Testing is the most important step in any software development workflow. This is even more important for software that will manage assets like tokens, cryptocurrency and identities. In one work blockchain dApps and contracts. In this post I show a typical development workflow to create smart contracts for the Ethereum blockchain and test them with Truffle, the most popular Ethereum development environment. In order to speed up the development, I regularly test the contracts on a local blockchain, provided by Ganache Truffle and Ganache can be easily installed via npm.

Install Truffle with $ npm install -g truffle

Install Ganache with $ npm install -g ganache-cli

For additional details about the installation procedure, following the instructions at the respective websites above will be just fine. In the following article I will be using command ganache-cli with default options and run a blockchain RPC on 8545, just what the official geth would do. Let's start writing the smart contract in Solidity.

The smart contract

The contract I am going to test is a simple escrow account that allows registrants to join a wallet by making a deposit of a certain amount of ETH. Before explaining the code, let’s create the workspace where the contract code will be saved, compiled and migrated from to the blockchain.

Make a directory and get in there

$ mkdir solidity_test
$ cd solidity_test

Initialize the contract plumbing with truffle $ truffle init

The contract code will be saved in file depositContract.sol under the $ ./contracts/ folder

$ cd contracts

$ touch depositContract.sol

And now the real stuff: solidity code. Let’s break down the entire smart contract in pieces that are easier to digest. Understanding what the smart contract does will help understanding what type of testing code should we be writing later.

Now we write some functions that can be called via transactions. These are the same functions we will be writing tests for. The code is heavily commented. In any case, to have a quick overview of which function implements what, a short description is hereby provided

Function Description add() adds a registrant and set her deposit as the value sent to the contract. A registrant can add any value she wants. But to become a validator, at least MIN_STAKE ethers are needed. The contract will keep track of the amounts added by each registrant and sum additional transactions until the value becomes higher than MIN_STAKE get_registrants() returns the list of all the addresses of current active registrants. That is all registrants who deposited at least MIN_STAKE getStake(address _registrant) given the address of a registrant, returns her current deposit getNumberRegistrants() returns the number of active registrants getBalance() returns the total amount of ether deposited by all registrants

Here is the solidity code that implements each function.

Once the smart contract is complete we can compile it with

$ truffle compile

If everything is alright we should not see any warning/error. This doesn’t mean the functionality of the contract is implemented correctly. That’s what testing is for. Before proceeding to testing the contract functions, let’s start a local blockchain with ganache. In a different terminal, launch ganache-cli This returns something like

Namely, it creates 10 accounts and provides the private keys of each, so that we can create transactions as if we were on a real blockchain. Needless to say, never ever use these accounts on a real blockchain.

In order to inform truffle about the blockchain to migrate the contract to, modify truffle.js in the root folder and add one of the addresses created by ganache-cli (this will be the address that instantiate the contract, hence the owner)

Compiling and building a contract means generating the ABI and the bytecode that implements the functionality. This is what will be migrated to the blockchain and executed on the EVM (upon transaction execution). Within the truffle framework the ABI and bytecode will be stored in a artifact under build/contracts/depositContract.json In order to create such an artifact, create a script that informs truffle of the solidity code to build. Such a script is saved under migrations/.

In the first terminal, type $ cd migrations Create file 2_deposit_contract.js and edit with

Testing the contract

Creating tests depends on the functionality implemented in a contract and of course its complexity. Under the directory ./test create file TestDepositContract.js. A convention I personally use is to name test files with a prefix (Test_) and the name of the contract to be tested. In a complex project we might be dealing with multiple contracts and knowing which file is testing what will save us a lot of time later (not to mention that it keeps code organized and easier to manage).

The tests I am going to implement in this specific case are

  1. add registrant to registrar
  2. get the number of registrants

The code is heavily commented and easy to understand. Just two tips, though. Assert will determine the failure/success of the test. There should be a 1:1 relation between test functions and contract functions: it is best practice to have one test function for every contract function to be tested. It is possible to use more contract functions in one test function, if the functionality is complex and utility contract functions are used for the assert instruction to evaluate. For example, in the code below, functions getBalance() and getStake() are not directly testing the success of adding a registrant to the wallet (getNumberRegistrants() does so in the second test function). However, getBalance() provides the condition to assert in the first test: if one registrant is not added to the wallet, the balance will not reach 86 ethers and the test will fail.

Conclusion

In this article I explained how to test smart contracts with solidity and truffle. Dedicating one testing function for each contract function is considered best practice to have a testing framework that is easy to manage and that scales with the number of smart contracts in a project.

Written by

Managing Director @ amethix.com Chief Software Engineer & Host @datascienceathome.com

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store