Dec 31, 2018

Unit Testing On Smart Contract with Truffle [Full Stack Ethereum Dapp Part-3]

In Part-2, we deployed our smart contract on local private Ethereum blockchain. Now, we will test out our Solidity smart contracts behaviour with unit testing with Truffle which uses Chai and Mocha frameworks. In web development lifecycle, unit testing is needed to do to ensure the code is working the way as it is expected. So let's start it by creating a test file by running following command:


truffle create test HelloWorld

It will create hello_world.js file in test folder. Replace the content with following simple test


var helloworld = artifacts.require('HelloWorld');
contract('HelloWorld', function(accounts) {
  let instance;
  before(async () => {
    instance = await helloworld.deployed();
  });
  it('Default message should be hello world',async () => {
    let message = await instance.getMessage.call({from: accounts[0]});           
    assert.equal(message, "Hello World","Incorrect message.");
  });
});

It will call getMessage method and check the output. Let's re-deploy code and test:


truffle develop
truffle(develop)> migrate
truffle(develop)> test

or you can use 'truffle test' command directly.

You will get following output:


truffle(develop)> test
Using network 'develop'.



  Contract: HelloWorld
    ✓ Default message should be hello world (59ms)


  1 passing (83ms) 

Let's add one more test method to test user defined name


 it('Should save name',async () => {
            let result = await instance.setName.sendTransaction('Ram',{from: accounts[0]}); 
            let message = await instance.getMessage.call({from: accounts[0]});           
            assert.equal(message, "Hello Ram","Incorrect message.");        
        });

Save the file and run test.


truffle(develop)> test
Using network 'develop'.



  Contract: HelloWorld
    ✓ Default message should be hello world
    ✓ Should save name (87ms)


  2 passing (149ms)

Add one more test to ensure default message comes for other accounts


 it('Should be default message for other accounts',async () => {
            let message1 = await instance.getMessage.call({from: accounts[0]});   
            let message2 = await instance.getMessage.call({from: accounts[1]});
            assert.equal(message1, "Hello Ram","Incorrect user message.");  
            assert.equal(message2, "Hello World","Incorrect message.");  
        });

Output:


truffle(develop)> test
Using network 'develop'.



  Contract: HelloWorld
    ✓ Default message should be hello world
    ✓ Should save name (90ms)
    ✓ Should be default message for other accounts (53ms)


  3 passing (210ms)

In case of empty name, setName should throw error. Let's create a method to test it.


it('Should throw error on empty name',async () => {
  try{
    let result = await instance.setName.sendTransaction('',{from: accounts[0]}); 
    assert.fail(true,false,"The function should throw error");  
  }
  catch(err){
      assert.include(String(err),'revert','throws different error');
  }
});

In case of error, it checks if revert word exists which is expected.

Complete Code

Here is the complete code:


var helloworld = artifacts.require('HelloWorld');
contract('HelloWorld', function(accounts) {
  let instance;
  before(async () => {
    instance = await helloworld.deployed();
  });
  it('Default message should be hello world',async () => {
    let message = await instance.getMessage.call({from: accounts[0]});           
    assert.equal(message, "Hello World","Incorrect message.");
  });

  it('Should save name',async () => {
    let result = await instance.setName.sendTransaction('Ram',{from: accounts[0]}); 
    let message = await instance.getMessage.call({from: accounts[0]});           
    assert.equal(message, "Hello Ram","Incorrect message.");        
  });

  it('Should be default message for other accounts',async () => {
    let message1 = await instance.getMessage.call({from: accounts[0]});   
    let message2 = await instance.getMessage.call({from: accounts[1]});
    assert.equal(message1, "Hello Ram","Incorrect user message.");  
    assert.equal(message2, "Hello World","Incorrect message.");  
  });

  it('Should throw error on empty name',async () => {
    try{
      let result = await instance.setName.sendTransaction('',{from: accounts[0]}); 
      assert.fail(true,false,"The function should throw error");  
    }
    catch(err){
        assert.include(String(err),'revert','throws different error');
    }
  });

});

Output:

ethereum-dapp

In this blog post, we implemented different test methods for our smart contract using Mocha and Chai frameworks and executed with Truffle successfully.

In next post, we will implement front-end app to interact the smart contract using Web UI.

Stay tuned and Happy Blockchain !!