What is ava?
AVA is a test runner for Node.js with a concise API, detailed error output, and process isolation that allows for fast and reliable testing. It supports asynchronous testing, and its design encourages writing tests that run concurrently.
What are ava's main functionalities?
Concurrent Test Execution
AVA runs tests concurrently, which can lead to faster test execution. The example shows two tests, one synchronous and one asynchronous, both of which will run concurrently.
const test = require('ava');
test('foo', t => {
t.pass();
});
test('bar', async t => {
const bar = Promise.resolve('bar');
t.is(await bar, 'bar');
});
Snapshot Testing
AVA supports snapshot testing, which allows you to save the output of your tests and compare it to future test runs. This is useful for ensuring that your code's output remains consistent over time.
const test = require('ava');
test('snapshot', t => {
const obj = {foo: 'bar'};
t.snapshot(obj);
});
Power Assert
AVA uses power-assert to provide detailed assertion messages, making it easier to understand why a test failed. The example shows a failing test with a clear assertion message.
const test = require('ava');
test('power assert', t => {
const x = 'foo';
t.is(x, 'bar');
});
Test Isolation
AVA ensures that each test runs in isolation, preventing side effects from affecting other tests. The example shows how `t.context` can be used to set up data for each test.
const test = require('ava');
test.beforeEach(t => {
t.context.data = {foo: 'bar'};
});
test('test 1', t => {
t.context.data.foo = 'baz';
t.is(t.context.data.foo, 'baz');
});
test('test 2', t => {
t.is(t.context.data.foo, 'bar');
});
Other packages similar to ava
mocha
Mocha is a feature-rich JavaScript test framework running on Node.js and in the browser, making asynchronous testing simple and fun. It provides a variety of interfaces (e.g., BDD, TDD) and supports both synchronous and asynchronous testing. Compared to AVA, Mocha is more flexible but requires more configuration to achieve similar functionality.
jest
Jest is a delightful JavaScript Testing Framework with a focus on simplicity. It works out of the box for most JavaScript projects and includes features like snapshot testing, a built-in mocking library, and code coverage. Jest is more opinionated than AVA and provides a more integrated experience, especially for React applications.
tape
Tape is a tap-producing test harness for Node.js and browsers. It is minimalistic and focuses on providing a simple API for writing tests. Tape does not include features like test runners or reporters, which makes it less feature-rich compared to AVA but also simpler and more lightweight.
Simple concurrent test runner
Even though JavaScript is single-threaded, IO in Node.js can happen in parallel due to its async nature. AVA takes advantage of this and runs your tests concurrently, which is especially beneficial for IO heavy tests. Switching from Mocha to AVA in Pageres brought the test time down from 31 sec to 11 sec. Having tests run concurrently forces you to write atomic tests, meaning tests that don't depend on global state or the state of other tests, which is a great thing!
Install
$ npm install --save-dev ava
Usage
Add it to package.json
{
"scripts": {
"test": "ava"
}
}
Create your test file
var test = require('ava');
test('foo', function (t) {
t.pass();
t.end();
});
test('bar', function (t) {
t.plan(2)
setTimeout(function () {
t.is('bar', 'bar');
t.same(['a', 'b'], ['a', 'b']);
}, 100);
});
Run it
$ npm test
CLI
$ ava --help
Usage
ava <file|folder|glob> [...]
Examples
ava
ava test.js test2.js
ava test-*.js
Default patterns when no arguments:
test.js test-*.js test/**
Documentation
Test files are just normal Node.js scripts and can be run with $ node test.js
. However, using the CLI is preferred for simplicity and future parallelism support.
Tests are run async and require you to either set planned assertions t.plan(1)
, explicitly end the test when done t.end()
, or return a promise.
You have to define all tests synchronously, meaning you can't define a test in the next tick, e.g. inside a setTimeout
.
Test anatomy
To create a test, you just call the test
function you require'd from AVA and pass in an optional test name and a callback function containing the test execution. The passed callback function is given the context as the first argument where you can call the different AVA methods and assertions.
test('name', function (t) {
t.pass();
t.end();
});
Optional test name
Naming a test is optional, but you're recommended to use one if you have more than one test.
test(function (t) {
t.end();
});
You can also choose to use a named function instead:
test(function name(t) {
t.end();
});
Planned assertions
Planned assertions are useful for being able to assert that all async actions happened and catch bugs where too many assertions happen. It also comes with the benefit of not having to manually end the test.
This will result in a passed test:
test(function (t) {
t.plan(1);
setTimeout(function () {
t.pass();
}, 100);
});
And this will result in an error because the code called more assertions than planned:
test(function (t) {
t.plan(1);
t.pass();
setTimeout(function () {
t.pass();
}, 100);
});
Promise support
If you return a promise in the test you don't need to explicitly end the test as it will end when the promise resolves.
test(function (t) {
return somePromise().then(function (result) {
t.is(result, 'unicorn');
});
});
Serial test execution
While concurrency is awesome, there are some things that can't be done concurrently. In these rare cases, you can call test.serial
, which will force those tests to run serially before the concurrent ones.
test.serial(function (t) {
t.end();
});
API
test([name], body)
test.serial([name], body)
name
Type: string
Test name.
body(context)
Type: function
Should contain the actual test.
context
Passed into the test function and contains the different AVA methods and assertions.
See the claim
docs for supported assertions.
plan(count)
Plan how many assertion there are in the test. The test will fail if the actual assertion count doesn't match planned assertions. When planned assertions are used you don't need to explicitly end the test.
Be aware that this doesn't work with custom assert modules. You must then call .end()
explicitly.
end()
End the test. Use this when plan()
is not used.
Tips
Temp files
Running tests concurrently comes with some challenges, doing IO is one. Usually, serial tests just create temp directories in the current test directory and cleans it up at the end. This won't work when you run tests concurrently as tests will conflict with each other. The correct way to do it is to use a new temp directory for each test. The tempfile
and temp-write
modules can be helpful.
FAQ
Why not mocha
, tape
, node-tap
?
Mocha requires you to use implicit globals like describe
and it
, too unopinionated, bloated, synchronous by default, serial test execution, and slow. Tape and node-tap are pretty good. AVA is highly inspired by their syntax. However, they both execute tests serially and they've made TAP a first-class citizen which has IMHO made their codebases a bit convoluted and coupled. TAP output is hard to read so you always end up using an external tap reporter. AVA is highly opinionated and concurrent. It comes with a default simple reporter and will in the future support TAP through a reporter.
License
MIT © Sindre Sorhus & Kevin Mårtensson