Getting Started
This guide will cover what you need to know to start using Test Environment, from installation of dependencies to configuration.
To learn instead how to migrate an existing Truffle project, check out our Migrating from Truffle guide.
If you’re new to smart contract testing, you should probably first look at our [guide for automated tests], and then come back here. |
Installing Dependencies
Unlike Truffle, Test Environment is a testing library: it provides utilities that help you write tests, but it doesn’t run your tests for you.
This is a good thing: it means you are free to use any test runner of your choice. Here we will use Mocha (along with Chai):
$ npm install --save-dev @openzeppelin/test-environment mocha chai
Head to our Choosing a Test Runner guide to learn about different test runners, their pros and cons, and how to use them. |
Writing Tests
Each test file should have a require
statement importing Test Environment. This will bring up the local blockchain where your tests will run, and provide utilities to interact with it.
const { accounts, contract } = require('@openzeppelin/test-environment');
The exports you will be using the most are accounts
and contract
.
accounts
This is an array with the addresses of all unlocked accounts in the local blockchain. They are also prefunded, so they can all be used to send transactions.
A good practice is to use array destructuring to give meaningful names to each account, such as:
const [ admin, purchaser, user ] = accounts;
This will let you write clearer tests by making it obvious which actor is executing each action:
const crowdsale = await Crowdsale.new({ from: admin });
await crowdsale.buyTokens(amount, { from: purchaser });
Unlike Truffle’s accounts array, the default address (the one that is used when you don’t provide one) is not included in accounts . This will help you avoid common pitfalls while writing tests. Head to the API reference to learn more.
|
contract
You will use this function to load your compiled contracts into JavaScript objects. By default these will be Truffle contracts, but they can be configured to be web3 contracts.
// Loads the built artifact from build/contracts/Crowdsale.json
const Crowdsale = contracts.fromArtifact('Crowdsale');
Test Cases
The overall structure of your tests will be determined not by Test Environment, but by your test runner of choice.
In Mocha, test cases are declared with the it
function, and grouped in describe
blocks.
We’ll use the following contract to provide an example. 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 { }
And here’s how testing this contract using Test Environment and Mocha looks like:
// 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);
});
});
In Choosing a Test Runner you can compare how this example looks in each test runner.
Running your Tests
Test Environment is not executable: tests are run by invoking your runner of choice directly. We recommend that you do this by adding a test
script to your package.json
.
This is what that looks like when using Mocha:
// package.json
"scripts": {
"test": "mocha --exit --recursive"
}
Mocha’s --exit flag is required when using Truffle contracts. Otherwise, the test suite will not exit. Learn more.
|
All set! Running npm test
to execute the test suite:
$ npm test
MyContract
✓ the deployer is the owner
Because we’re running Mocha directly, it is very easy to pass additional options to it. Try the following, which will cause Mocha to stop immediately on the first failing test:
|
Compiling your Contracts
Test Environment requires your contracts to be compiled: you can use Truffle to do this.
$ npx truffle compile
Compilation artifacts will be stored in the build/contracts
directory, where Test Environment (and most other tools) will read them from.
You can set your project to recompile all contracts when running tests by adding this step to your
|
Using OpenZeppelin Test Helpers
Complex assertions, such as testing for reverts or events being emitted, can be performed by using the OpenZeppelin Test Helpers.
When used alongside Test Environment, there is no need for manual configuration: require
the helpers and use them as usual.
Configuration
Multiple aspects of Test Environment can be configured. The default values are very sensible and should work fine for most testing setups, but you are free to modify these.
To do this, create a file named test-environment.config.js
at the root level of your project: its contents will be automatically loaded.
// test-environment.config.js
module.exports = {
accounts: {
amount: 10, // Number of unlocked accounts
ether: 100, // Initial balance of unlocked accounts (in ether)
},
contracts: {
type: 'truffle', // Contract abstraction to use: 'truffle' for @truffle/contract or 'web3' for web3-eth-contract
defaultGas: 6e6, // Maximum gas for contract calls (when unspecified)
// Options available since v0.1.2
defaultGasPrice: 20e9, // Gas price for contract calls (when unspecified)
artifactsDir: 'build/contracts', // Directory where contract artifacts are stored
},
node: { // Options passed directly to Ganache client
gasLimit: 8e6, // Maximum gas per block
gasPrice: 20e9 // Sets the default gas price for transactions if not otherwise specified.
},
};
Advanced Options
These settings are meant to support more complex use cases: most applications will not require using them.
setupProvider
async function setupProvider(baseProvider)
Returns a new web3 provider that will be used by all contracts and helpers.
Often used to wrap the base provider in one that performs additional tasks, such as logging or Gas Station Network integration:
// test-environment.config.js
module.exports = {
setupProvider: (baseProvider) => {
const { GSNDevProvider } = require('@openzeppelin/gsn-provider');
const { accounts } = require('@openzeppelin/test-environment');
return new GSNDevProvider(baseProvider, {
txfee: 70,
useGSN: false,
ownerAddress: accounts[8],
relayerAddress: accounts[9],
});
},
};
fork
and unlocked_accounts
These options allow Test Environment’s local blockchain to fork an existing one instead of starting from an empty state. By using them you can test how your code interacts with live third party protocols (like the MakerDAO system) without having to deploy them yourself!
In forked mode, you will also be able to send transactions from any of the unlocked_accounts
(even if you don’t know their private keys!).
// test-environment.config.js
module.exports = {
node: { // Options passed directly to Ganache client
fork: 'https://mainnet.infura.io/v3/{token}@{blocknumber}, // An url to Ethereum node to use as a source for a fork
unlocked_accounts: ['0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B'], // Array of addresses specifying which accounts should be unlocked.
},
};
allowUnlimitedContractSize
Allows unlimited contract sizes. By enabling this flag, the check within the EVM for contract size limit of 24kB (see EIP-170) is bypassed. Useful when testing unoptimized contracts that wouldn’t be otherwise deployable.
Enabling this flag causes Ganache to behave differently from production environments. |
// test-environment.config.js
module.exports = {
node: { // Options passed directly to Ganache client
allowUnlimitedContractSize: true, // Allows unlimited contract sizes.
},
};