Testing with Solidity

As we mentioned before, Truffle enables us to run unit tests using Solidity as well as JavaScript. In order to start testing with Solidity, create a file called TontineTest.sol (.sol extension not .js) in Truffle’s test/ folder with the following code:

 import "truffle/Assert.sol";
import "../contracts/tontine.sol";
import "truffle/DeployedAddresses.sol";
contract TontineTest { }

This is an empty test contract, so we start by importing the needed testing libraries, DeployedAddresses.sol and Assert.sol (created dynamically at the time of deployment), along with the contract being tested (tontine.sol).

When we import external contracts, filenames are always treated as a path with "/" as the directory separator. All path names are treated as absolute paths unless they start with . or ....

Let’s go over how to write a Solidity unit test. In the TontineTest contract, define the following testing methods:

contract TontineTest {
uint public initialBalance = 10 ether;
Cplayer cplayer_;
Ctontine tontine;

function beforeEach() public {
cplayer_ = Cplayer(DeployedAddresses.Cplayer());
tontine = Ctontine(DeployedAddresses.Ctontine());
}

function testplayer() public {
cplayer_.AddPlayer("Player1", 1234);
bool expected = cplayer_.exist(this);
Assert.isTrue(expected, "Player doesn't exist");
}

function testjoingame() public {
cplayer_.AddPlayer("Player1", 1234);
uint expectedBalance = 2 ether;
tontine.join.value(2 ether)();
Assert.equal(expectedBalance, address(tontine).balance, "Contract balance should be 2 ether");
}
}

It's a bit of a long test, but let's look at it after disassembling.

First off, the initialBalance variable indicates to Truffle how much ether to allocate to this test contract on deployment (10 ethers, in our case).

In just the same way as we did in the JavaScript tests, we can define a beforeEach hook as well as beforeAll, afterAll, and afterEach to perform setup and teardown actions before and after each test is run. We also use DeployedAddresses objects to manage the address of the contracts deployed at test time.

In this example, we are performing two unit tests:

  • testplayer(): Checks whether a player added using addPlayer() has been successfully added
  • testjoingame(): Checks whether the Ctontine contract has accepted funds deposited through the join() method

Similarly to the JavaScript tests, we rely on assertions (ChaiJS) to perform our unit tests. Various testing functions, such as equals(), greaterThan(), isFalse(), and isEqual(), are defined in Truffle’s Assert library.

In the first test, we use the this variable, which represents the address of the current contract. Why? Because when we call the addPlayer() method, the Cplayer contract will see the test contract as the sender. Thus, we add the contract as a player, and then we check whether a player has been created with the same address.

In the second test, we add a player and then call the join() method while also depositing two ethers into the game. Afterward, we check whether the contract balance is equal to the deposited amount.

Run the tests again in the same way we did before: truffle test test/TontineTest.sol --network my_ganache.

In the test output, you should see the tests successfully passed, as follows:

Building a test suite while developing new smart contracts is an absolute necessity. I would suggest looking at the tests provided by the OpenZeppelin framework. You will find their cleanly written and reusable test patterns.

Compared to JavaScript, Solidity presents limited testing features. For example, unlike JavaScript tests, Solidity tests provide us with a single account (contract account), which can be used as the transaction sender.

Hopefully, this section helps you get started with your tests. Alright, after validating execution correctness with our tests, let's walk through how to spot potential bugs with debugging.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset