Smart Contract Testing & Ethereum Simulator

In my last tutorial, I show Solidity programming language a bit and what it feels like to develop a simple HelloWorld smart contract. Today, I am talking about testing which is an important topic in development, especially for the smart contract development.

All codes in this tutorial are available in this repo


Two facts about smart contract

Two specific facts in smart contract development that you need to know before I jump in to today’s topic.

  1. Solidity doesn’t natively provide logging functions such as console.log() function in Javascript/NodeJS or print() function in python.
  2. The smart contract is executed by Ethereum virtual machine(VM). So we always need a node or a piece of the Ethereum network to execute the contract. With that being said, you cannot run to execute a contract in an intuitive manner locally like you did with node, golang, or others.
$ node ./filename.js  -> okay
$ go ./filename.go -> okay
$ solc ./contract.sol -> not available

I hope you can understand the difficulty of logging and executing a smart contract now.


Options to execute a contract

Here are some options to execute your contract.

  1. Deploy the contract to live(the real) Ethereum main network and execute it. Cost about 5 minutes and real money(slow, public(dangerous), $$$) to deploy and execute.
  2. Deploy the contract to testnet (for developers’ usage) Ethereum network and execute it. Cost about 2 minutes to deploy and free ether(slow, public(dangerous), free)
  3. Deploy to an Ethereum network simulator and execute it. Cost about 3 seconds and free(fast, private(nice!!), free)

In a development lifecycle which we usually want to do unlimited deploying, testing, updating… for many times (let’s say, probably 100+ times in an afternoon.). Then option 3 is the best option. And it is what I am sharing with you in this tutorial. (I will teach you about option 1, 2 in the future post).


Preparation

Installation packages

To prepare, please do following commands to download truffle and ethereumjs-testrpc

$ npm i -g truffle ethereumjs-testrpc

Prepare contract

download this repo and go to 02-Testing/start

You should see the files like this

.
├── contracts
│ ├── ConvertLib.sol
│ ├── HelloEthSalon.sol
│ ├── MetaCoin.sol
│ └── Migrations.sol
├── migrations
│ ├── 1_initial_migration.js
│ └── 2_deploy_contracts.js
├── test
│ ├── TestMetacoin.sol
│ └── metacoin.js
└── truffle.js

and the HelloEthSalon contract should look like this, and this is what we build in the last tutorial.

pragma solidity ^0.4.13;
contract HelloEthSalon {
  string message = 'Hello Ethereum Salon!';
  function HelloEthSalon() {
    // constructor
  }
  function GetMessage() returns (string) {
    return message;
  }
}

Let’s write a testing file in Javascript

Yes, the most popular way to test a Smart Contract is writing a testing file in Javascript (Javascript wins again).

In the HelloEthSalon contract, we have a function GetMessage() that returns a string of Hello Ethereum Salon!. Let’s write a testing file to test if the function will actually do the job.

In the 02-Testingfolder, use truffle create test command to start a testing file.

$ truffle create test HelloEthSalon

As you can see, the new folder structure should like this:

.
├── contracts
│ ├── ConvertLib.sol
│ ├── HelloEthSalon.sol
│ ├── MetaCoin.sol
│ └── Migrations.sol
├── migrations
│ ├── 1_initial_migration.js
│ └── 2_deploy_contracts.js
├── test
│ ├── TestMetacoin.sol
│ ├── hello_eth_salon.js
│ └── metacoin.js
└── truffle.js

Hope you notice that we have a new file called hello_eth_salon.js under the test folder. The file looks like following,

contract('HelloEthSalon', function(accounts) {
  it("should assert true", function(done) {
    var hello_eth_salon = HelloEthSalon.deployed();
    assert.isTrue(true);
    done();
  });
});

Here, we thanks truffle for two things:

  1. It smartly helps us naming the testing file as hello_eth_salon.js when we call the contract HelloEthSalon .
  2. This testing file looks like Mocha-style testing. Yes, we are using Mocha under the hood for smart contract testing.

If you want, I recommend you to split your editor to two sessions. One with the contract itself and other is for writing test file.

Let’s write the testing file as below,

var HelloEthSalon = artifacts.require('./HelloEthSalon.sol');
contract(‘HelloEthSalon:GetMessage’, function(accounts) {
  it(“should return a correct string”, function(done) {
    var hello_eth_salon = HelloEthSalon.deployed();
    hello_eth_salon.then(function(contract){
      return contract.GetMessage.call(); // **IMPORTANT
    }).then(function(result){
      assert.isTrue(result === ‘Hello Ethereum Salon!’);
      done();
    })
  });
});

here are four important points to check:

  1. don’t forget to import the contract from ‘./HelloEthSalon.sol’ in the first line.
  2. HelloEthSalon.deployed() is a Promise; please help yourself familiar with Promise, because you will use promise a lot in smart contract development since interacting with Ethereum network is always an async behavior, which mean data will come back at some point later.
  3. In line 7, I have an important notes, please remember to use contract.GetMessage.call(); instead usingcontract.GetMessage(); directly
  4. The assertion statement assert.isTrue(result === ‘Hello Ethereum Salon!’); is checking if the result we get from contract.GetMessage.call(); is equal to a string of Hello Ethereum Salon!.

Next, do the truffle test command in the 02-Testing folder,

$ truffle test ./test/hello_eth_salon.js

What did you see? Here we go, we got a really normal/typical error!

Could not connect to your Ethereum client. Please check that your Ethereum client:
- is running
- is accepting RPC connections (i.e., "--rpc" option is used in geth)
- is accessible over the network
- is properly configured in your Truffle configuration file (truffle.js)

We will fix this together in the next session.


Start your Ethereum Simulator

When you do the truffle command, it tries to connect to a Ethereum node, and it, by default, tried to talk a local node on port localhost:8545 by a RPC manner, and notes 8545 is the conventional port for ethereum-node.

Here is some explanation about RPC(Remote procedure call) in case you want to learn more.

In distributed computing, a remote procedure call (RPC) is when a computer program causes a procedure (subroutine) to execute in a different address space (commonly on another computer on a shared network), which is coded as if it were a normal (local) procedure call, without the programmer explicitly coding the details for the remote interaction.
(source: wikipedia)

Eventually, Ethereum just follows the RPC method to have a port that something can talk to. Let’s start a ethereum-rpc on your machine. Just do command testrpc in a new terminal session

$ testrpc

and it will see it starts as normal:

EthereumJS TestRPC v4.0.1 (ganache-core: 1.0.1)
Available Accounts
==================
(0) 0x6e7b9b253fa4903039d43c8f998e8dbf82c3d008
(1) 0x528e44414f21aff2f8981c760c277626844a28b8
(2) 0x627ecc39f7922f68f14f10c725c9b8bc4bededd5
(3) 0x0c096e2c028b03a991fac711a69e75e958767913
(4) 0x9c11555a97368d300865da5e974217b00321dcf7
(5) 0xfb68a14c5c440db114962f1c1d6a7959cd488a50
(6) 0x1a9d092749e95af2117ef13633bd25f264968c45
(7) 0xde089c1b4d8d0f038229becbe4ab7d24f6c667a4
(8) 0x323a64b3ff3a38b8005a91d9824271f1f2c060e9
(9) 0x117002f45fb2cc6450be81de84fb5f6b682df5c4
Private Keys
==================
(0) b346735e69c99c13de1ad0204096ebd6457a38ca3269f669be220ff3cd0e1ef7
(1) 907f9937025a3a48d68f43eb57a3fe35928b2326fcbd6b5d693a6aee8dbf6f44
(2) 362c567d4ea2e191d9b03292b8f0152e248d8d1b069f6ff566c41117db8188ce
(3) d673dfecd0d5bbd269b1f07118a572aa158e9a036c9ef0049422b13cba8ed190
(4) 2f5a5c8864630fc380530c9060ca094a14b8e97916593d425bc0483fea4dae4b
(5) 8b67b723e626d1afe0410c34b5276ac634af2c33a3307aa8528d4d9dc097ea74
(6) 65d47a5c49bb057c6a92455b295a94963538a26d987669b660cbba0432ae1f17
(7) 73d74c54c58b83b797c821e8735b4570b672894bfb9c7e331d494922122d5184
(8) 1236f027bbea4d0ab522ce35a0d5ef55de8abce050d3d922911f6eeb8ab3b6aa
(9) da88126c119b8f7d6c8635f32fb5087f692235d1a14cfe11d034a0cbbd321081
HD Wallet
==================
Mnemonic: melody depend hello wing pyramid jacket use skirt utility electric obtain then
Base HD Path: m/44'/60'/0'/0/{account_index}
Listening on localhost:8545

It will start with some fake accounts and private keys, and available on localhost:8545. If you see the localhost:8545 , congrats, move to next step, if not, message me, I am willing to help.(It should usually work)


Let’s test it.

Again, do the truffle test command in the 02-Testing folder,

$ truffle test ./test/hello_eth_salon.js

First, you see it compile and … and…. Boom, another Error:

Compiling ./contracts/HelloEthSalon.sol...
Contract: HelloEthSalon:GetMessage
1) should return a correct string
> No events were emitted
0 passing (46ms)
1 failing
1) Contract: HelloEthSalon:GetMessage should return a correct string:
Uncaught Error: HelloEthSalon has not been deployed to detected network (network/artifact mismatch)

The error says HelloEthSalon has not been deployed to detected network .

Okay, so the thing is that besides making the testrpc up, we also need to deploy our contract to the brand-newtestrpc Ethereum Simulator.

What we need to do is to update our 2_deploy_contracts.js file under migrations folder. It should be looked

var ConvertLib = artifacts.require("./ConvertLib.sol");
var MetaCoin = artifacts.require("./MetaCoin.sol");

module.exports = function(deployer) {
   deployer.deploy(ConvertLib);
   deployer.link(ConvertLib, MetaCoin);
   deployer.deploy(MetaCoin);
};

Let’s import our HelloEthSalon contract right after the MetaCoin importing statement and add a new deploy call right after the deployer.deploy(MetaCoin); . The updated deploy javascript file should be this:

If you are done, again, do the truffle test command in the 02-Testing folder

$ truffle test ./test/hello_eth_salon.js

First, you see it compile and do the testing as following:

Compiling ./contracts/HelloEthSalon.sol...
Contract: HelloEthSalon:GetMessage
✓ should return a correct string (69ms)
1 passing (82ms)

if you see the ✓ checking mark, congrats, we finish your first ever smart contract testing.

In case you need, here is the whole testcase codes:


Update contract and create a new testing case.

Let’s change the message global variable in he HelloEthSalon contract. Change the message to “I know testing of a contract!!”, yes two exclamation points. And deploy it again by truffle deploy command.

$ truffle deploy

And go to update the test case, to check if the message is what we expect.

$ truffle test ./test/hello_eth_salon.js

If you see the ✓ checking mark, congratulations, you now know to build a safer contract by implementing testing.

The new contract should look like

And the updated testing file should look like


Lastly, I recently have a blockchain startup called Ventured. In case that you may be interested. Please feel free to

Visit our site: https://ventured.one/

Join our slack: https://slack.ventured.one/

Like us on Facebook: https://www.facebook.com/venturednetwork

Check our GitHub: https://github.com/venturednetwork


If you enjoyed this article, please share with your developer/designer friends and click the green “❤” heart below and so others can find it too. Thanks for reading.