Testing your smart contract with Truffle

kevinmaarek

Maarek

Posted on December 31, 2021

Testing your smart contract with Truffle

As you might know testing is very important, especially when working with smart contract and blockchain, as anything in the blockchain is immutable. There is ways to make upgrades to a contract but we’ll get to that in another post.

For this article, I’ll be working with the fundraiser contract I wrote in my last post. Here is the full code:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Fundraiser {
    address public owner;
    address[] public backers;

    constructor() {
        owner = msg.sender;
    }

    function sendMoney() public payable {
        require(msg.value > 0, "No Ether were sent.");
        backers.push(msg.sender);
    }

    function getBalance() public view returns (uint256) {
        return address(this).balance;
    }

    function endFundraising() public {
        require(msg.sender == owner, "Only the owner is allowed to end the fundraising.");
        payable(owner).transfer(address(this).balance);
    }
}
Enter fullscreen mode Exit fullscreen mode

And I’m assuming you’ve used truffle init to setup your project, meaning, you have a test directory in your workspace. This directory should be empty by now.

Creating a test file

We’ll start by creating a new file called fundraiser.js. We are going to write our test in Javascript. If you are familiar with it, Truffle uses the Mocha testing framework, with a few differences, it uses contract() instead of describe().

That contract method is just used to logically group the tests by contract. Before executing your tests, the function make sure the contract is redeployed to the running environment.

Here, I’m assuming you have Ganache installed and running.

So in this empty JavaScript file, we will first get our compiled contract and call the contract method to start writing test cases:

const Fundraiser = artifacts.require("./Fundraiser.sol");

contract("Fundraiser", (accounts) => {

});
Enter fullscreen mode Exit fullscreen mode

The contract method has two parameter, the first one would be the name of the contract and the second one is a function that gives you a parameter with a list of account that your environment provided for testing. That’s in that function that we will describe out test cases with Mocha’s it function.

Just like contract, two parameters, a description of the test and the actual test.
We can describe as many tests as we need.

const Fundraiser = artifacts.require("./Fundraiser.sol");

contract("Fundraiser", (accounts) => {
    it("some test", async () => {
        // ...
    });
    it("some other test", async () => {
        // ...
    });
});
Enter fullscreen mode Exit fullscreen mode

Interacting with our contract

While testing a contract you will need to interact with it. The first thing you would want is to access an instance. From the artifact, you can call the .deployed().

const contract = await Fundraiser.deployed();
Enter fullscreen mode Exit fullscreen mode

To access a public variable from your contract, we will use the call() function.

const owner = await contract.owner.call();
Enter fullscreen mode Exit fullscreen mode

Finally, to call a public function just call the function like you would to on any object.

let balance = await contract.getBalance();
Enter fullscreen mode Exit fullscreen mode

To add a value or specify the caller’s address, add an optional object :

await contract.sendMoney({from: '0x0...', value: 1});
Enter fullscreen mode Exit fullscreen mode

Note: value is in Gwei (1 Gwei = 0,00000001 Ether), to send Ether, just multiply the value by 10 to the power of 18 (10**18 in JavaScript).
You can also the gas and some data as parameter. More on the Truffle documentation.

Now you should have anything needed to test our contract.

Writing some tests

The first one will verify if the backers array get properly populated after a backer would have sent some Ether.

    it("check the participants array after participation", async () => {
        // Get the first account
        let account = accounts[0];
        // Retreive the deployed contract
        const contract = await Fundraiser.deployed();
        // Make the account send 14 gwei
        await contract.sendMoney({from: account, value: 14});
        // Get the particpant at index 0
        let backer = await contract.backers.call(0);
        // Check if that participant is the same as the addess we used
        assert.equal(backer, account);
    });
Enter fullscreen mode Exit fullscreen mode

We will write another test to check the balance increase.

    it("check the balance increase", async () => {
        // Get the first account
        let account = accounts[0];
        // Retreive the deployed contract
        const contract = await Fundraiser.deployed();
        // Get the balance before the operation
        let balanceBefore = await contract.getBalance();
        // Send 1 gwei
        await contract.sendMoney({from: account, value: 1});
        // Get the balance after the gwei was sent
        let balanceAfter = await contract.getBalance();
        // Check if the balance after is equal to the balance before plus 1
        assert.equal(balanceAfter.toString(), parseInt(balanceBefore.toString())+1);
    });
Enter fullscreen mode Exit fullscreen mode

One last test to check the end of the fundraising.

    it("check the balance after the fundraising ends", async () => {
        // Retreive the deployed contract
        const contract = await Fundraiser.deployed();
        // Get the contract owner by accessing the owner attribute
        const contractOwner = contract.owner.call();
        let contractsBalanceBefore = await contract.getBalance();
        let ownersBalanceBefore = await web3.eth.getBalance(contractOwner);
        // End the fund raising
        await contract.endFundraising({from: contractOwner});
        let contractsBalanceAfter = await contract.getBalance();
        let ownersBalanceAfter = await web3.eth.getBalance(contractOwner);
        // Check if the contract balance is now zero
        assert.equal(contractsBalanceAfter, 0);
        // Check if the owner account received the contract's balance
        assert.equal(ownersBalanceBefore >= ownersBalanceAfter+contractsBalanceBefore, true);
    });
Enter fullscreen mode Exit fullscreen mode

Looks like we now have a decent code coverage for our contract.

We can just run the tests with the command truffle test.

Dont hesitate to check out the amazing documentation from the truffle website. You can also check the Mocha documentation.

Feel free to comment if you have any question, or contact me via Twitter.

Happy coding!

💖 💪 🙅 🚩
kevinmaarek
Maarek

Posted on December 31, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related