Testing Solidity Smart Contracts
Tags: ethereum, solidity, testing, javascript
Published: 2021-08-04 21:34:34

In the [previous blog](https://dev.to/nazeeh21/integrate-your-react-app-with-smart-contracts-4o3m), we discussed deploying our Smart Contract to the **Rinkeby Test Network** and interacting with it from our front end. 

I decided to write this article on tests as a bonus. You should write these tests before writing the Smart Contract and integrating with the front end.

One way of testing our Contract is interacting with it manually and testing every scenario. In this, one needs to consider every scenario and test the Contract in that case. Remembering every edge case could be tedious. Also forgetting a single edge case could break our Contract. 

This is not a preferred way of testing. Instead, we should write tests for our Smart Contracts. Writing tests will check our Smart Contract in every possible scenario and ensures our Contract behaves expectedly. If any of the test cases fail, we can fix that edge case during production only. Thus, writing tests for Smart Contracts is necessary.

So, let's write tests for our [Lottery Contract](https://dev.to/nazeeh21/develop-your-first-dapp-26dj). 

#### Note: To Follow along, refer to this [repo](https://github.com/Nazeeh21/Lottery-Contract/blob/main/test/Lottery.test.js).

Navigate to the `lottery-contract` directory we created in the [previous blog](https://dev.to/nazeeh21/integrate-your-react-app-with-smart-contracts-4o3m) and create an empty directory named `test`. Inside the `test` directory, create an empty `Lottery.test.js` file.

For writing tests, we need to add a dependency to our project. For that run the following command in your root directory.

```javascript
yarn add -D ganache-cli mocha
```

We have all pre-requisites for writing tests. Now, head on to the `Lottery.test.js` file and paste the following code.

```javascript
const assert = require('assert');
const ganache = require('ganache-cli');
const Web3 = require('web3');

const web3 = new Web3(ganache.provider());
const { interface, bytecode } = require('../compile.js');

```

`ganache-cli` is a fast and customizable blockchain emulator that allows us to make calls to the blockchain without the overheads of running an actual Ethereum node. This will help us in performing our tests instantaneously.

For creating an instance of `web3`, we have used the provider from `ganache` as we will be deploying our Contract to the `ganache` local network for testing. Apart from `web3`, we have required `assert` (a native javascript library) and **interface** along with the **bytecode** from our **compiled Contract**.

Now, paste the following code.

```javascript
let accounts;
let lottery;

beforeEach(async () => {
  accounts = await web3.eth.getAccounts();
  lottery = await new web3.eth.Contract(JSON.parse(interface))
    .deploy({ data: bytecode })
    .send({ from: accounts[0], gas: '1000000' });
});
```

We have declared two variables namely `accounts` and `lottery` that will store our **accounts** and **lottery instance** respectively.

After that, we have declared the `beforeEach()` function which will execute before every test case. In this function, we are fetching the accounts from the `web3` instance and storing them in the `accounts` variable.  After that, we have deployed the local instance of our Contract using `web3` and stored it in the `lottery` variable.

Now, let's write our first test.

```javascript
describe('Lottery Contract', () => {
  it('deploys a contract', () => {
    assert.ok(lottery.options.address);
  });
}
```

Above we have defined `describe()` function. It allows us to gather our tests into separate groupings within the same file, even multiple nested levels. 

In the first argument of our `describe()` function, we have passed the name of our test-suite i.e. 'Lottery Contract'. Inside our `describe()` function, we have declared an `it()` function, inside which we have written our test. This test will ensure that our Contract gets deployed successfully.

The first argument of the `it()` function will accept the name of our test and the second argument will accept the function which runs our test. In this function, we have written `assert.ok()` which ensures that the value passed inside this function is not null.

We have written our first test. Now, let's run our test. For that navigate to the root directory and run the following command in the terminal.

```
yarn test
```

You should see the following output in your terminal. 

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1628104176760/g5tVvNaVp.png)

The **tick** in front of the test name indicates that our test has successfully passed.
Congratulations! You have written your first test.

Now let's write tests for other scenarios as well. 

For that paste the code from below inside the `describe()` function.

```javascript
  it('allows 1 account to enter', async () => {
    await lottery.methods.enter().send({
      from: accounts[0],
      value: web3.utils.toWei('0.02', 'ether'),
    });

    const players = await lottery.methods.getPlayers().call({
      from: accounts[0],
    });

    assert.strictEqual(accounts[0], players[0]);
    assert.strictEqual(1, players.length);
  });
```

This test will check whether our Lottery allows users to enter the Lottery. For that, we are initially entering the lottery by calling the `lottery.methods.enter()` method. Following that, we are fetching the players of the lottery by calling the `lottery.methods.getPlayers()` method. Our `players` variable will be an array containing the addresses of all the players of the contract.

Now, we have called the `assert.strictEqual()` method which ensures that both the arguments passed to it are strictly equal. This test will ensure that we can enter our Lottery successfully.

Similarly, we'll check for multiple accounts to enter our lottery. For that paste the code from below.

```javascript
   it('allows multiple accounts to enter', async () => {
    await lottery.methods.enter().send({
      from: accounts[0],
      value: web3.utils.toWei('0.02', 'ether'),
    });

    await lottery.methods.enter().send({
      from: accounts[1],
      value: web3.utils.toWei('0.02', 'ether'),
    });

    await lottery.methods.enter().send({
      from: accounts[2],
      value: web3.utils.toWei('0.02', 'ether'),
    });

    const players = await lottery.methods.getPlayers().call({
      from: accounts[0],
    });

    assert.strictEqual(accounts[0], players[0]);
    assert.strictEqual(accounts[1], players[1]);
    assert.strictEqual(accounts[2], players[2]);
    assert.strictEqual(3, players.length);
  });
```

In this test, we are entering the lottery from multiple accounts and after that, we are ensuring that each player can enter the lottery or not by calling the `assert.strictEqual()` method.

After this, we'll write a test to ensure that users can't enter with ethers less than the required amount to enter the lottery. For that, paste the code below.

```javascript
  it('requires minimum amount of ether to enter', async () => {
    try {
      await lottery.methods.enter().send({
        from: accounts[0],
        value: 0,
      });
      assert(false);
    } catch (err) {
      assert(err);
    }
  });
```

This test will make sure that the test fails when a user tries to enter with ethers less than the required amount and pass the test when the user is unable to enter the lottery.

Following this, we will write a test that tests that only the manager could able to pick a winner.  For that, paste the below code.

```javascript
it('only manager can pick winner', async () => {
    try {
      await lottery.methods.pickWinner().send({
        from: accounts[1],
      });
      assert(false);
    } catch (err) {
      assert(err);
    }
  });
```

Remember in our `beforeEach()` function, we have deployed the contract using `accounts[0]`. Thus, the `address` of our manager is the address stored at `accounts[0]`. As a result, our test should fail if we try to pick a winner from an account other than `accounts[0]`.

This test will ensure that only our manager is allowed to pick a winner.

At last comes our final test, which ensures that ethers are sent to the winner of the Contract.

For that, paste the below test.

```javascript
it('sends money to the winner and resets the players array', async () => {
    await lottery.methods.enter().send({
      from: accounts[0],
      value: web3.utils.toWei('2', 'ether'),
    });

    const initialBalance = await web3.eth.getBalance(accounts[0]);

    await lottery.methods.pickWinner().send({
      from: accounts[0],
    });

    const finalBalance = await web3.eth.getBalance(accounts[0]);

    const difference = finalBalance - initialBalance;
    console.log('difference: ', difference);
    assert(difference > web3.utils.toWei('1.8', 'ether'));

    const players = await lottery.methods.getPlayers().call({ from: accounts[0]})

    assert.strictEqual(0, players.length)
  });
```

This test ensures that lottery ethers are sent to the winner of the lottery on picking up the winner by the manager of the contract.

We have successfully written all the necessary tests. Now let's run these tests.  For that, navigate to the root directory and run the `yarn test` command.

You should see the following output in the terminal.

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1628111071730/8raIOOwQD.png)

Congratulations! You have successfully written tests for your Smart Contract. These tests provide security that your Contract won't break out.
Now we can be more reliable on your Smart Contracts and can be 100% sure that our Smart Contract will not misbehave.

Connect with me on [twitter](https://twitter.com/Nazeeh21).