Blazing fast smart contract testing. One-line setup for an awesome testing experience.
- Near-instant start up: have your code running in under 2s after typing
npm test
. - Test runner agnostic – from the familiarity of Mocha, to parallel tests using Jest or Ava!
- Non-opinionated: use either
@truffle/contract
orweb3-eth-contract
as you see fit. - First class support for the OpenZeppelin Test Helpers.
- Highly configurable: from gas limit and initial balance, to complex custom web3 providers.
- No global variables, no hacks.
test-environment
is the result of our learnings while developing the OpenZeppelin Contracts, combining best practices and the tools we've come to rely on over the years. We think you'll love it!
npm install --save-dev @openzeppelin/test-environment
Simply require('@openzeppelin/test-environment')
in your test files, and it will take care of all Ethereum-related tasks. A local ganache-powered blockchain with unlocked accounts will be spinned up, and all tools will be configured to work with it.
const { accounts, contract } = require('@openzeppelin/test-environment');
const [ tokenHolder ] = accounts;
const ERC20 = contract.fromArtifact('ERC20'); // Loads a compiled contract
async function test() {
const token = await ERC20.new({ from: tokenHolder });
const initialBalance = await token.balanceOf(tokenHolder);
}
Note: if you'd rather not rely on truffle contracts and use web3 contract types directly, worry not: you can configure test-environment
to use the web3-eth-contract
abstraction.
test-environment
is a testing library, something you use in your tests, as opposed to what actually runs them. For that, you are free 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.
Head to our test runners guide to learn more about how to setup each one.
test-environment
is not a contract compiler: for that, you'll want to use the OpenZeppelin CLI.
npm install --save-dev @openzeppelin/cli
npx oz compile
Compilation artifacts will be stored in the build/contracts
directory, where testing-environment
(and most other tools) will read them from.
OpenZeppelin Test Helpers support
testing-environment
does not include the Test Helpers library, but it will automatically detect it and configure it if it is installed. Simply require('@openzeppelin/test-helpers')
and use it as you already do.
The default options are very sensible and should work fine for most testing setups, but you are free to modify these. Simply create a file named test-environment.config.js
at the root level of your project: its contents will be automatically loaded.
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)
},
blockGasLimit: 8e6, // Maximum gas per block
};
Despite Truffle's design and goals being different from test-environment
's (one is an all-out development framework and the other a testing library), it is still quite simple to migrate from a truffle test
-based suite. Doing the whole process on the OpenZeppelin Contracts repository took less than thirty minutes!
Because truffle test
uses a lightly modified Mocha as a test runner (bundled with Chai for assertions), these two make best choice for a simple migration:
npm install --save-dev mocha chai
Don't forget to make Mocha the entry point of your test suite once you install it:
// in package.json
"scripts": {
- "test": "npx truffle test"
+ "test": "npx mocha --exit --recursive test"
}
It is now time to modify the test files themselves. The changes are few, but important:
- Add
require('@openzeppelin/test-environment')
to access the variables exported by the library:accounts
,contract
,web3
, etc. truffle test
automagically imports Chai: you will need torequire
it and set it up manually- Replace all instances of
artifacts.require
forcontract.fromArtifact
- Replace all intances of
truffle test
'scontract
function with a regular Mochadescribe
. You can still access the accounts array inaccounts
That's it! Let's see how a full migration might look like:
+const { accounts, contract, web3 } = require('@openzeppelin/test-environment');
// Setup Chai for 'expect' or 'should' style assertions (you only need one)
+const { expect } = require('chai');
+require('chai').should();
-const ERC20 = artifacts.require('ERC20');
+const ERC20 = contract.fromAbstraction('ERC20');
-contract('ERC20', function (accounts) {
+describe('ERC20', function () {
...
}
You are now ready to start using test-environment
by running npm test
. Enjoy lightning fast testing!
test-environment
exposes a number of variables that are used to interact with the local testing blockchain it setups. These are described in detail here:
const { accounts, defaultSender, contract, web3, provider, isHelpersConfigured } = require('@openzeppelin/test-environment');
accounts: string[]
An array of strings with the addresses of the accounts available for testing. By default, there are 10 unlocked accounts with 100 ETH each, but this can be configured.
const [ sender, receiver ] = accounts;
await myToken.transfer(receiver, 100, { from: sender });
defaultSender: string
A special account that is used by contracts created via contract
when no account is specified for a transaction (i.e. there is no explicit from
). This account is not included in accounts
to prevent accidental bugs during testing: whenever you want an account to make an action (deploy a contract, transfer ownership, etc.) you should be explicit about the sender of the transaction:
const [ owner ] = accounts;
// The depoloyment will be made by 'defaultSender' (not 'owner'!), making it
// the contract's owner
const myContract = await Ownable.new();
// And the following test will fail
expect(await myContract.owner()).to.equal(owner);
contract.fromArtifact: (contract: string) => any;
contract.fromABI: (abi: object, bytecode?: string | undefined) => any;
The contract
object is in charge of creating contracts from compilation artifacts. It does this via two functions:
fromArtifact
looks for a.json
file in thebuild/contracts
directory (equivalent to Truffle'sartifact.require
)fromABI
receives an ABI object directly, useful when the full compilation artifacts are not available
They both return instances of either @truffle/contract (by default) or web3-eth-contract, depending on configuration.
const ERC20 = contract.fromArtifact('ERC20');
const myToken = await ERC20.new(initialBalance, initialHolder);
A web3
instance, connected to the local testing blockchain. Useful to access utiltiies like web3.eth.sign
, web3.eth.getTransaction
, or web3.utils.sha3
.
A web3
provider, connected to the local testing blockchain. Used in more advanced scenarios, such as creation of custom web3
or ethers
instances.
isHelpersConfigured: boolean
A boolean indicating if the OpenZeppelin Test Helpers library was autodetected and configured.
Released under the MIT License.