Smart Contract Testing with Coverage

juwita
HARA Engineering
Published in
4 min readDec 20, 2018

Another story for smart contract testing series! As I promised before, this time we will use a real case smart contract and test coverage. The smart contract will involve a common use case in blockchain, ERC20 Token. We will try to create our own ERC20 Token using OpenZeppelin and test it with solidity-coverage library.

All code used in this post can be found on Github.

OpenZeppelin

OpenZeppelin is a library for secure smart contract development. It provides implementations of standards like ERC20 and ERC721 which you can deploy as-is or extend to suit your needs, as well as Solidity components to build custom contracts and more complex decentralized systems.

https://github.com/OpenZeppelin/openzeppelin-solidity

Installing OpenZeppelin is easy, as simple as installing node library. I’m using OpenZeppelin version 2.0.0.

npm install — save-exact openzeppelin-solidity@2.0.0

Contract

We will create a ERC20 token contract with these requirement:

  • The contract name will be your favorite food. In my case, I’m using “SushiToken”
  • The token is Ownable and ERC20 using OpenZeppelin.
  • The token can be burnt.
  • The token can be minted.
  • The token will have one custom function. Example: setTotalSushiEaten that will store every kind of sushi you ever eat.

Now we have OpenZeppelin installed, let’s code!

Code lines description:

[1] Define your solidity compiler version. This contract use version 0.4.25.

[3–4] Import OpenZeppelin library. I’m only using ERC20 and Ownable, but you can always explore other likes ERC20Mintable.sol, ERC20Capped.sol, etc.

[7–10] Details about your ERC20 token: the name of your token, symbol of your token, decimal of your token, and initial value of the token.

[12] Storage for the total kinds of sushi that you have already eaten

[14–16] Events that will be emitted on Burn function and Mint function and Set Sushi Total function, separately.

[18–20] A constructor function. When the contract deploys, the address that deploys the contract will get all the initial value token.

[22–25] Burn function. This function uses _burn function from ERC20.sol. The burn function will decrease the sender token and decreasing the total supply of token.

[27–30] Mint function. This function uses _mint function from ERC20.sol and using onlyOwner modifier so only the contract owner can transact this function. The mint function is the opposite of the burn function. It will increase the destination address token and totalSupply.

[32–36] Our custom function! setTotalSushiEaten will add totalSushiEaten storage according to the sushi type.

Solidity Coverage

Solidity Coverage is code coverage library for Solidity Testing. If you familiar with IstanbulJS, this library use IstanbulJS to run the coverage.

To use Solidity Coverage on our truffle,we need to add coverage network on truffle.js (or truffle-config.js). Or you can define other options using .solcover.js.

This configuration above is the default configuration that you can use. I’m using “localhost” host because I will use testrpc launched by solidity coverage.

Testing

All the testing library we need is already included on the docker base image. We only need to add OpenZeppelin installation on Dockerfile.

For docker_compose_test.yml, the difference between my latest post is only the command. We add command below for running the test and coverage test:

bash -c "truffle test — network development && ./node_modules/.bin/solidity-coverage"

Things we test are:

  • The token details: name, symbol, decimal, initial supply and total supply.
  • If addresses with token can transfer token to another address.
  • If addresses which have token can burn the token
  • If the Owner address can mint the token.
  • If addresses can set the totalSushiEaten storage.

Running Test!

After completing all the preparations, run your test with docker compose command:

docker-compose -f docker_compose_test.yml up — abort-on-container

In the logs, you can see that the test runs two times. Once for the usual testing and once for coverage test. The results in the logs are same with IstanbulJS result.

Currently all of our test-cases are passes, but the coverage result told us there are lines (28, 29, 33, 34) that are still uncovered. This is because we created the test-case without adding exactly what we wanted to test. We fix this by filling in the test-cases to fulfill the line coverage.

Run the docker compose up command and look a the result. Clear 100%

Notes:

  • In my experience, using testrpc network that isspawned by Solidity Coverage itself is more stable for coverage test than using testrpc that we define before. This is because in some cases Solidity Coverage needs more gas when it’s using another testrpc. Other FAQs about Solidity Coverage can be found here.
  • The test reporter created by Solidity Coverage are only html, lcov, and text. If you use my base image, it will report on cobertura instead lcov.

Join our community on Telegram!

--

--