This chapter covers how to test smart contract. Since blockchain transaction is not reversible, testing is very important before deployment.
Testing with Truffle
Truffle provides an automated testing framework. This framework lets you write simple and manageable tests in two different ways:
In Javascript and TypeScript, for exercising your contracts from the outside world, just like application.
In Solidity, for exercising your contracts in advances, bare-to-the-metal scenarios.
1) Get Started
Add a setter function setGreet to the contract for testing purpose. The source code is given below.
pragmasolidity ^0.5.6;contract Mortal {/* Define variable owner of the type address */addresspayable owner;/* This function is executed at initialization and sets the owner of the contract */constructor () public { owner = msg.sender; }/* Function to recover the funds on the contract */functionkill() publicpayable { if (msg.sender == owner) selfdestruct(owner); }}contractMEVerseGreeterisMortal {/* Define variable greeting of the type string */string greeting;/* This runs when the contract is executed */constructor (stringmemory_greeting) public { greeting = _greeting; }/* Main function */functiongreet() publicviewreturns (stringmemory) {return greeting; }/* Newly added function for testing. */functionsetGreet(stringmemory_greeting) public {// only owner can change greeting messagerequire(msg.sender == owner,"Only owner is allowed."); greeting = _greeting; }}
Test Scenario
Whether greet() function returns "Hello, MEVerse" properly
Whether setGreet() function returns new greeting message properly and reverts when non-owner account attempts to update the greeting.
First, we will install the Chai assertions library (or any different assertions library you use) for generic assertions, and the truffle-assertions library for the smart contract assertions.
npminstall--save-devchaitruffle-assertions
2) Writing test in Solidity
Testing with Solidity can be a little bit more intuitive than JavaScript tests. Solidity test contracts live alongside JavaScript tests as .sol files.
Create a file called TestMEVerseGreeting.sol in the test folder. The Truffle suite provides helper libraries for testing, so let's take a look at the example Solidity test:
DeployedAddresses : Every time you change your contract, you must redeploy it to a new address. You can get the deployed contract addresses through this library.
Assert : It gives us access to various testing functions, like Assert.equals(), Assert.greaterThan(), etc.
$truffletestUsingnetwork'test'.Compilingyourcontracts...===========================> Compiling ./test/TestMEVerseGreeting.sol> Artifacts written to /var/folders/8k/pj9b6lld4qn4hq1l7h97y9pm0000gn/T/test--5158-mS50YzRSpyUZ> Compiled successfully using:-solc:0.5.16+commit.9c3226ce.Emscripten.clangTestMEVerseGreeting1) testGreetingMessage>Noeventswereemitted0passing (3s)1failing1) TestMEVerseGreetingtestGreetingMessage:Error:greetingmessageshouldmatch (Tested: Hello,MEVerse,Against:HelloMEVerse)atcheckResultForFailure (/Users/meverselabs/.nvm/versions/node/v16.16.0/lib/node_modules/truffle/build/webpack:/packages/core/lib/testing/SolidityTest.js:66:1)atrunMicrotasks (<anonymous>)atprocessTicksAndRejections (node:internal/process/task_queues:96:5)atContext.<anonymous> (/Users/meverselabs/.nvm/versions/node/v16.16.0/lib/node_modules/truffle/build/webpack:/packages/core/lib/testing/SolidityTest.js:92:1)
It failed. Let's take a look at the error message.
Error: greeting message should match (Tested: Hello, MEVerse, Against: Hello MEVerse)
Notice the missed ',(comma)' at string memory expectedGreet = "Hello MEVerse".
Fix the code and run the test again.
$truffletestUsingnetwork'test'.Compilingyourcontracts...===========================> Compiling ./test/TestMEVerseGreeting.sol> Artifacts written to /var/folders/8k/pj9b6lld4qn4hq1l7h97y9pm0000gn/T/test--5231-DBNS4Mgo7Mo0> Compiled successfully using:-solc:0.5.16+commit.9c3226ce.Emscripten.clangTestMEVerseGreeting✓testGreetingMessage (41ms)1passing (3s)
Test success!
If you want to test your contract on Testnet, use network option.
$truffletest-networkMEVerseTestnetUsingnetwork'test'.Compilingyourcontracts...===========================> Compiling ./contracts/MEVerseGreeter.sol> Compiling ./test/TestMEVerseGreeting.sol> Artifacts written to /var/folders/8k/pj9b6lld4qn4hq1l7h97y9pm0000gn/T/test--5324-jtF8M5Qz82cV> Compiled successfully using:-solc:0.5.16+commit.9c3226ce.Emscripten.clangTestMEVerseGreeting✓testGreetingMessage (55ms)1passing (3s)
3) Writing test in JavaScript
Truffle uses the Mocha testing framework and Chai assertion library to provide a solid framework for JavaScript test. JavaScript test gives you more flexibility and enables you to write more complex tests.
Let's create a file and name it 0_MEVerseGreeting.js under test directory.
The test code is:
// Interacting directly with MEVerseGreeter contractconstMEVerseGreeter=artifacts.require("./MEVerseGreeter.sol");consttruffleAssert=require('truffle-assertions');contract("MEVerseGreeter",async(accounts) => {// store the contract instance at a higher level // to enable access from all functions.var MEVerseGreeterInstance;var owner = accounts[0];var greetMsg ="Hello, MEVerse";// This will run before each test proceed.before(asyncfunction() {// set contract instance into a variable MEVerseGreeterInstance =awaitMEVerseGreeter.new(greetMsg, {from:owner}); })it("#1 check Greeting message",asyncfunction() {// set the expected greeting messagevar expectedGreeting = greetMsg;var greet=awaitMEVerseGreeterInstance.greet();assert.equal(expectedGreeting, greet,"greeting message should match"); })it("#2 update greeting message.",asyncfunction() {var newGreeting ="Hi, MEVerse";awaitMEVerseGreeterInstance.setGreet(newGreeting, { from:owner });var greet =awaitMEVerseGreeterInstance.greet();assert.equal(newGreeting, greet,"greeting message should match"); });it("#3 [Failure test] Only owner can change greeting.",asyncfunction() {var fakeOwner = accounts[1]; awaittruffleAssert.fails(MEVerseGreeterInstance.setGreet(greetMsg, { from:fakeOwner })); });});
Run test file 0_MEVerseGreeting.js)
$truffletest./test/0_MEVerseGreeting.jsUsingnetwork'test'.Compilingyourcontracts...===========================> Compiling ./contracts/MEVerseGreeter.sol> Artifacts written to /var/folders/8k/pj9b6lld4qn4hq1l7h97y9pm0000gn/T/test--5527-DYxpp0UInYnm> Compiled successfully using:-solc:0.5.16+commit.9c3226ce.Emscripten.clangContract:MEVerseGreeter✓#1 check Greeting message✓#2 update greeting message. (42ms)✓#3 [Failure test] Only owner can change greeting. (176ms)3passing (301ms)