Tips for Unit Testing Ethereum Smart Contracts in Solidity
By: Sangmoon Oh
Tags: Solidity, Smart Contract, Ethereum, Programming, Truffle

ECMAScript 8 (2017)

It has been quite some time since JavaScript was born, and it is still evolving rapidly. JavaScript was standardized by ECMAScript and a new version of the specification has been announced every year since 2015¹ ².

For more efficient working on Truffle test programs, it is important to select the proper version of JavaScript that contains useful features for testing.

JavaScript is asynchronous by nature. Most frameworks and libraries, including web3.js and Truffle contract abstraction, run asynchronously. Programming flows with async processes handling callbacks or promises may be beneficial for performance, but they can be more complex and difficult. For the test program, readability and easiness can be more important than optimization or performance.

The async ³/ await ⁴ statements enable synchronous flows of asynchronous functions so the code can avoid callback stacking. The await statement is valid only in an async block. In the case of the Truffle test, the test function second argument of the it() function would be the async function, and then an await call would be made inside the test function.

The below sample shows all calls to the token contract( Token.new , token.mint , token.totalSupply , token.transfer ) are await inside an async function ( async() => {} ).

it("...", async() => {

const chance = new Chance();

const admin = chance.pickone(accounts);

const token = await Token.new('Color Token', 'RGB', {from: admin}); let balance = 0;

for(const acct of accounts){

balance = toBN(1E19).muln(chance.natural({min:1,max:100}));

await token.mint(acct, balance, {from: admin});

} const total = await token.totalSupply();



const loops = 20;

let sender = 0, recipient = 0, delta = 0;

for(let i = 0; i < loops; i++){

sender = chance.pickone(accounts);

recipient = chance.pickone(accounts);

delta = toBN(1E13).muln(chance.natural({min:0,max:100})); await token.transfer(recipient, delta, {from: sender});

assert.isTrue((await token.totalSupply()).eq(total));

}

});

In JavaScript, variable declaration using the var keyword has unusual semantics such as function scoping and hoisting⁵. This is not a common feature in other programming languages and can make non-native JavaScript programmers frustrated even with simple codes. ECMAScript 6, unveiled in 2015, introduced const ⁶ and let ⁷ statements to compensate for those unexpected effects of var . Variable declaration using const and let has block scoping and no hoisting⁸. Hoisting can actually be more complex behind the scene⁹. But virtually there is no hoisting for const and let when compared with var . So it is strongly recommended to use const and let , if function scoping or hoisting is intended.

To make variable declaration and usage even less error-prone, using strict mode¹⁰ is also recommended. It is enough to add the 'use strict' literal at the beginning line of the contract() function. This simple line will remove many error-prone old features and make you feel more comfortable.

contract("ERC20Regular Contract Test Suite", async accounts => {



"use strict";



if(accounts.length > 8){ // avoid too many accounts

accounts = (new Chance()).pickset(accounts, 8);

} ...

)};

The async / await statements were added in ECMAScript 8 and const / let in ECMAScript 6. Node.js 9.11.2 or higher supporting ECMAScript 8 almost fully¹¹ recommended utilizing those statements.