Choosing a Test Runner

Test Environment is only a testing library: something you use in your tests, but not what actually runs them.

This is very much by design: it gives you the freedom to use any regular JavaScript test runner.

We recommend picking one of the following:

  • Mocha: simple and straightforward, the easiest way to get started when migrating from truffle test.

  • Jest: the most popular runner out there, featuring lightning speed, parallel tests, and extensive guides.

  • Ava: a minimalistic runner with parallel tests and support for ES6 and TypeScript.

Both Jest and Ava have their own assertions library, but for Mocha, you may want to also use Chai.

Example Contract

To showcase the different runners, we will write tests for the following contract using each one.

It consists of a simple access control mechanism via OpenZeppelin Contracts' Ownable:

// contracts/MyContract.sol

pragma solidity ^0.5.0;

import "@openzeppelin/contracts/ownership/Ownable.sol";

contract MyContract is Ownable { }

Mocha & Chai

Test cases are declared with the it function, and grouped in describe blocks:

// test/MyContract.test.js

const { accounts, contract } = require('@openzeppelin/test-environment');

const { expect } = require('chai');

const MyContract = contract.fromArtifact('MyContract');

describe('MyContract', function () {
  const [ owner ] = accounts;

  beforeEach(async function {
    this.myContract = await MyContract.new({ from: owner });
  });

  it('the deployer is the owner', async function () {
    expect(await this.myContract.owner()).to.equal(owner);
  });
});

To install Mocha and Chai, run:

$ npm install --save-dev mocha chai

All tests in the test directory are then executed with:

$ npx mocha --recursive test --exit
Mocha’s --exit flag is required when using Truffle contracts. Otherwise, the test suite will not exit. Learn more.

Mocha will run tests sequentially, waiting for each to finish before starting the next one. A single local blockchain will be created, and shared by all tests.

Parallel Tests

Unlike Mocha, both Jest and Ava will let you run tests in parallel: each test file will be executed at the same time, leading to massive time savings when running a large test suite.

When using these runners, Test Environment will create an independent local blockchain for each parallel run, so there is zero risk of your tests unexpectedly interacting with each other.

Migrating from Mocha to Jest is rather straightforward, and is well worth the effort if you are spending much time waiting for tests to finish.

Jest

Jest looks very similar to Mocha (describe, it and beforeEach are all there), but there are two big differences:

  • You don’t need to use Chai, since Jest has its own assertion library (see the official documentation)

  • You cannot store objects in this inside beforeEach

// test/MyContract.test.js

const { accounts, contract } = require('@openzeppelin/test-environment');

const MyContract = contract.fromArtifact('MyContract');
let myContract;

describe('MyContract', function () {
  const [ owner ] = accounts;

  beforeEach(async function {
    myContract = await MyContract.new({ from: owner });
  });

  it('the deployer is the owner', async function () {
    expect(await myContract.owner()).toEqual(owner);
  });
});
If migrating from Mocha, you can still use Chai to reduce the number of changes you need to make to your tests. Just be careful not to get Chai’s and Jest’s expect mixed up!

To install Jest, run:

$ npm install --save-dev jest

Jest will execute all tests in files matching \*.test.js with:

$ npx jest ./test

Ava

Ava is a new, modern test runner, product of learnings on the JavaScript ecosystem over the years. As such, it may look different from what you’re used to, but it is a great tool that is worth learning how to use. Their documentation is a great starting point.

// test/MyContract.test.js

import test from 'ava';

import { accounts, contract } from '@openzeppelin/test-environment';
const [ owner ] = accounts;

const MyContract = contract.fromArtifact('MyContract');

test.before(async t => {
  t.context.myContract = await MyContract.new({ from: owner });
});

test('the deployer is the owner', async t => {
  t.is(await myContract.owner(), owner);
});

To install Ava, run

$ npm install --save-dev ava

Ava will execute all tests in all files in the test directory with:

$ npx ava