Blockchain Tests with Truffle (Solidity)

Hello guys !! After my talk about Testing Blockchain Applications on the #TechKnowDay,  I thought it was worth posting a guide on the same topic as I felt it was complex to also understand Blockchain. So, this is a quick tutorial on how to run tests with Truffle Framework.

If you are still wondering where you can use Blockchain, here it’s a table with the percentage of companies that are focusing on it and the use cases.

Requirements

We are going to use a bit of Javascript for the web part and Solidity for the blockchain project. For this project you will need to have installed NPM and Node already. We are going to install Truffle as part of the setup. You will also need MetaMask and Ganache, all of them you can find bellow:

Setup your Local Ethereum Blockchain

  • Open your MetaMask plugin and click on new Custom RPC Network. Type the following (Currency Symbol will be automatically populated after you type the Chain ID) and save.
  • Open Ganache, click on Quick Start (Ethereum). Double check if the server has this configuration clicking on the on top right and then Server tab.
  • Click on Show keys of the account you have created and copy the private key.
  • Back on MetaMask, click on Import Account and add the private key you have copied from the previous step.

Setting up the project

On your terminal run:

npm install -g truffle

If you want to follow the test steps only, then you need to download the first release that contains the installation files of the project already:

https://github.com/rafaelaazevedo/PetShopTestWorkshop/releases/tag/1.0

If you want to try the installation for yourself from the scratch, then just go to this link and follow the Getting Started guide. This guide won’t be focusing on the installation of the framework or the setup of the contracts and migrations.

Just a heads up that for this project I am using the pet-shop box instead of the MetaCoin from the Getting Started guide.

Installation

After downloading the project, you will need to run some commands to set it up. Open your terminal on the root of the project and run:

Run the development console

truffle develop

Compile and migrate the smart contracts. Inside the development console you don’t need to type the truffle command.

compile
migrate

To get out of the truffle console type

.exit

Create a new file inside of the test folder called TestAdoption.sol and import all the needed modules, such as assertions, new instances of deployed addresses and the contract that will be tested.

pragma solidity >=0.5.0;

import "truffle/Assert.sol";
import "truffle/DeployedAddresses.sol";
import "../contracts/Adoption.sol";

Then create the class and inside add the variables:

  • adoption that will get the Adoption contract that was deployed
  • expectedId this one will be the id of the pet under test (You can get the id of the pet you want to test from this file)
  • expectedAdopter which is the address of this current contract that we are creating in the next function
contract TestAdoption {
    Adoption adoption = Adoption(DeployedAddresses.Adoption());
    uint256 expectedPetId = 8;
    address expectedAdopter = address(this);
}

Testing

Asserting the adoption

  • Add the testUserCanAdoptPet() function below the variables block.
  • Note that here you are creating a function that will test you can adopt a pet and for this you will need to get the address related to the adoption transaction and compare the returnedId with the expectedPetId address
  • Try to explore and add other asserts like checking if it’s not returning null.
function testUserCanAdoptPet() public {
        uint256 returnedId = adoption.adopt(expectedPetId);

        Assert.equal(
            returnedId,
            expectedPetId,
            "Adoption of the expected pet should match what is returned."
        );
    }

Asserting the adopter of the pet

  • Add the testGetAdopterAddressByPetId() function below the previous function.
  • This function will check if the adopter address for that pet is the same from the adopters list
  • Try to explore and add other asserts like comparing the age of the pet is returning correctly, for that you would need to add the age on the Adoption.sol contract, then compile and migrate again.
function testGetAdopterAddressByPetId() public {
        address adopter = adoption.adopters(expectedPetId);

        Assert.equal(
            adopter,
            expectedAdopter,
            "Owner of the expected pet should be this contract"
        );
    }

Asserting the list of adopters

  • Now add the testGetAdopterAddressByPetIdInArray() function below the previous function.
  • This function will check if the memory address for this petId is the same as the expectedAdopter
  • Almost the same test as before, but this time we are explicitly storing adopters in memory rather than contract’s storage and then comparing them.
function testGetAdopterAddressByPetIdInArray() public {
        address[16] memory adopters = adoption.getAdopters();

        Assert.equal(
            adopters[expectedPetId],
            expectedAdopter,
            "Owner of the expected pet should be this contract"
        );
    }
}

You should have something like this:

pragma solidity >=0.5.0;

// The first two imports are referring to global Truffle files, not a `truffle` directory.
// Gives us various assertions to use in our tests.
import "truffle/Assert.sol";

// When running tests, Truffle will deploy a fresh instance of the contract being tested to the blockchain.
import "truffle/DeployedAddresses.sol";

// The smart contract we want to test.
import "../contracts/Adoption.sol";

contract TestAdoption {
    // The address of the adoption contract to be tested
    Adoption adoption = Adoption(DeployedAddresses.Adoption());

    // The id of the pet that will be used for testing
    uint256 expectedPetId = 8;

    //The expected owner of adopted pet is this contract
    address expectedAdopter = address(this);

    // Testing the adopt() function
    function testUserCanAdoptPet() public {
        uint256 returnedId = adoption.adopt(expectedPetId);

        Assert.equal(
            returnedId,
            expectedPetId,
            "Adoption of the expected pet should match what is returned."
        );
    }

    // Testing retrieval of a single pet's owner
    function testGetAdopterAddressByPetId() public {
        address adopter = adoption.adopters(expectedPetId);

        Assert.equal(
            adopter,
            expectedAdopter,
            "Owner of the expected pet should be this contract"
        );
    }

    // Testing retrieval of pet owner storing getAdopters in memory
    function testGetAdopterAddressByPetIdInArray() public {
        // Store adopters in memory rather than contract's storage
        address[16] memory adopters = adoption.getAdopters();

        Assert.equal(
            adopters[expectedPetId],
            expectedAdopter,
            "Owner of the expected pet should be this contract"
        );
    }
}

Running the tests

  • Open your terminal on the root of the project and run:
truffle test
  • If everything went okay you will see green checks on your terminal like this:

You can check the final code with the latest release (Spoiler alert: You will see pictures of my dog, my dog’s best friend, my friend’s cat and my previous dog)

git clone git@github.com:rafaelaazevedo/PetShopTestWorkshop.git

Open a tab on your terminal and run the development local server

npm run dev

You can also check truffle config file to check the host and port where we are running the project and edit if you need to.

Releases of the project:

  • Release 1.0 Contains the Solidity contracts and Truffle Migrations only (No UI)
  • Release 1.1 Contains the Solidity contracts, Truffle Migrations and Truffle Initial Tests only (No UI)
  • Release 1.2 Contains the Solidity contracts, Truffle Migrations, Truffle Initial Tests and Initial UI
  • Release 1.3 Contains the Solidity contracts, Truffle Migrations, Truffle Initial Tests, Final version of UI

Blockchain Testing Tools

If you are wondering what Blockchain is, I will give you a quick introduction. Blockchain is a data structure that is distributed at once in many different places and as you can’t ever delete from it, it is extremely difficult to make amendments. This makes the record more secure and more trustworthy.

So, what are the kinds of test that you can perform ? You can use the traditional testing, since it is just normal development work with normal testing criteria. So, boundary value analysis, decision tables, test driven development and behavior driven development techniques.

There is also a set of questions that can help you to build your test scenarios, for example:

  • How does it handles valid and invalid inputs?
  • How does it cope with a wide range of input data?
  • How does it handle missing state, or existing state?
  • How does it handle error cases?
  • How does it handle security and access control?

You don’t need to test the Blockchain because the algorithms are well-established, because it is a distributed system, but the transactions still require some kind of validation. For example, you may need to check if your transaction is valid before it can be approved. There are approval authorities for different blockchains, and they must test the integrity of the transactions.

 

What is Smart Contract ?

Smart Contract is an API and defines the rules for transactions in a Blockchain network. A Smart Contract is a set of rules in the form of programmable constructs that are capable of automatically enforcing themselves when pre-defined conditions are met.

It has public functions which might be called by anyone registered on the Blockchain network. However, unlike a traditional API, a Smart Contract cannot call external web APIs.

 

What do you need to know to test Ethereum Smart Contracts ?

Test automation requires that the platform being tested must have hooks so that external automated scripts can instruct the platform, observe the outcome, and verify that the outcome is what is expected. Legacy platforms in banking often do not have these hooks, and that makes automation much more difficult. When you compare smart contracts to older software used in banks, you can automate testing much earlier and much faster.

 

I will show some of the tools that you can use to perform tests on Blockchain applications:

 

Ethereum Tester

This github has a project for you to test Ethereum Blockchain Applications. You will need to clone Eth-Tester. The Eth-tester library strictly enforces some input formats and types.

 

Truffle

Truffle is one of the most popular Ethereum development frameworks and has testing functionality, it is a scaffolding framework for smart contracts used by UI builders. You have the ability to write automated tests for your contracts in both JavaScript and Solidity and get your contracts developed quickly.

Ganache

Ganache is the most-used library for testing Ethereum contracts locally. It mocks a blockchain that gives you access to accounts you can run tests, execute commands, etc.

 

Populus

By default tests run against an in-memory ethereum blockchain and as you can see here Populus supports writing contracts that are specifically for testing.

 

Manticore

Manticore is a symbolic execution tool for analysis of binaries and smart contracts. It is supported on Linux and requires Python 2.7. Ubuntu 16.04 is strongly recommended. It has a Python programming interface which can be used to implement custom analyses. You can see more about here on the wiki.

 

Hyperledger Composer

Hyperledger Composer supports three types of testing: interactive testing, automated unit testing and automated system testing. It’s a framework for rapidly building Blockchain business networks on top of a Blockchain platform, such as Hyperledger Fabric.

This framework allows you to automatically deploy the business network definition, and then interact with it by submitting queries and transactions in order to test how that business network really behaves.

 

Corda Testing Tools

Corda is a blockchain-inspired, open-source distributed ledger platform. There are several distinct test suites each with a different purpose: Unit tests, Integration tests, Smoke tests, Load tests and other. These tests are mostly written with JUnit and can run via Gradle.

 

BitcoinJ

This tool BitcoinJ allows you to interact with Bitcoin connecting directly to the bitcoin network. So, you can simulate send and receive transactions in real time, also you don’t need to have a local copy of the Bitcoin Core.

You can follow this guide to get start with this tool.

 

Resources:

https://www.joecolantonio.com/2018/02/01/blockchain-testing-tools/

https://joecolantonio.com/testtalks/175-blockchain-application-testing-rhian-lewis/

http://searchsoftwarequality.techtarget.com/answer/Heres-everything-you-need-to-know-about-testing-blockchain

https://www.capgemini.com/2017/01/testing-of-smart-contracts-in-the-blockchain-world/

http://www.bcs.org/content/conWebDoc/56020

https://medium.com/@mrsimonstone/test-your-blockchain-business-network-using-hyperledger-composer-c8e8f112da08