### Recipes/Babel # Configuring Babel with AVA Translations: [Français](https://github.com/avajs/ava-docs/blob/main/fr_FR/docs/recipes/babel.md) AVA is ESM-only. [`@babel/register`](https://babeljs.io/docs/en/babel-register) only hooks legacy `require()` loading, so this setup is no longer supported. If you need Babel, precompile your files before running AVA or use an ESM-capable loader configured through [`nodeArguments`](../06-configuration.md#node-arguments). --- ### Recipes/Browser Testing # Setting up AVA for browser testing AVA is running in a __Node.js__ environment. JavaScript that runs in a browser will likely expect the browser DOM globals to be in place. With help from a package called [jsdom](https://github.com/jsdom/jsdom), you can write unit tests with `ava` also for JavaScript that will run in a browser and relying on browser specific globals such as `window`, `document` and `navigator`. ## Install jsdom ```bash npm install --save-dev jsdom ``` ## Writing unit tests Use `jsdom` to set the globals and the DOM elements that the test target is expecting. ### An example Unit Test The JavaScript code to be tested is doing a DOM query, such as: `document.querySelector('#my-element-id')`. To make the code testable with `ava`, add the element to `jsdom` and set the global object. ```js import test from 'ava'; import { JSDOM } from 'jsdom'; test.before(() => { const dom = new JSDOM('
'); // insert any html needed for the unit test suite here global.document = dom.window.document; // add the globals needed for the unit tests in this suite. }); test('this is an example', (t) => { const res = myTarget.runFunctionThatExpectsTheDocumentGlobalAndElement(); t.truthy(res); }); ``` ## Important note In general, adding globals to the `Node.js` environment is [recommended against](https://github.com/jsdom/jsdom/wiki/Don't-stuff-jsdom-globals-onto-the-Node-global) by `jsdom`. Please read through the linked wiki page and make sure you understand why. --- ### Recipes/Code Coverage # Code coverage Translations: [Español](https://github.com/avajs/ava-docs/blob/main/es_ES/docs/recipes/code-coverage.md), [Français](https://github.com/avajs/ava-docs/blob/main/fr_FR/docs/recipes/code-coverage.md), [Italiano](https://github.com/avajs/ava-docs/blob/main/it_IT/docs/recipes/code-coverage.md), [日本語](https://github.com/avajs/ava-docs/blob/main/ja_JP/docs/recipes/code-coverage.md), [Português](https://github.com/avajs/ava-docs/blob/main/pt_BR/docs/recipes/code-coverage.md), [Русский](https://github.com/avajs/ava-docs/blob/main/ru_RU/docs/recipes/code-coverage.md), [简体中文](https://github.com/avajs/ava-docs/blob/main/zh_CN/docs/recipes/code-coverage.md) Use [`c8`] to compute the code coverage of your tests. First install [`c8`]: ``` $ npm install --save-dev c8 ``` At its simplest run AVA through [`c8`]. In your `package.json` file: ```json { "scripts": { "test": "c8 ava" } } ``` You may want to exclude the `coverage` directory from source control. Assuming you're using Git, add the following to your `.gitignore` file: ``` coverage ``` [`c8`]: https://github.com/bcoe/c8 --- ### Recipes/Debugging With Chrome Devtools # Debugging tests with Chrome DevTools Translations: [Français](https://github.com/avajs/ava-docs/blob/main/fr_FR/docs/recipes/debugging-with-chrome-devtools.md) You can debug your tests using [Chrome DevTools](https://developers.google.com/web/tools/chrome-devtools). Open Chrome, then navigate to . Click the *Open dedicated DevTools for Node* link within the *Devices* section. In the *DevTools for Node* window, navigate to *Sources* and in the left-hand column select *Filesystem*. Add your project directory to the workspace. Make sure to grant permission. Now run a specific test file: ```console npx ava debug test.js ``` The DevTools should connect automatically and your tests will run. Use DevTools to set breakpoints, or use the `debugger` keyword. Run with the `--break` option to ensure the DevTools hit a breakpoint right before the test file is loaded: ```console npx ava debug --break test.js ``` By default the inspector listens on `127.0.0.1:9229`. You can customize the host and the port: ```console npx ava debug --host 0.0.0.0 --port 9230 test.js ``` You'll have to add a connection for this port in the *Connection* tab. --- ### Recipes/Debugging With Vscode # Debugging tests with Visual Studio Code Translations: [Français](https://github.com/avajs/ava-docs/blob/main/fr_FR/docs/recipes/debugging-with-vscode.md) You can debug your tests using [Visual Studio Code](https://code.visualstudio.com/). ## Debugging with the debug terminal You can use VS Code's “JavaScript Debug Terminal” to automatically debug AVA run on the command-line. 1. From the Command Palette (F1 or command + shift + p / control + shift + p), run `Debug: JavaScript Debug Terminal` 2. Run `npx ava` in the terminal ## Creating a launch configuration Alternatively you can create a launch configuration, which makes it easier to debug individual test files. 1. Open a workspace for your project. 1. In the sidebar click the *Debug* handle. 1. Create a `launch.json` file. 1. Select the Node.js environment. 1. Add following to the `configurations` array and save changes: ```json { "type": "node", "request": "launch", "name": "Debug AVA test file", "program": "${workspaceFolder}/node_modules/ava/entrypoints/cli.js", "args": [ "${file}" ], "outputCapture": "std", "console": "integratedTerminal", // optional "skipFiles": [ "/**/*.js" ] } ``` ### Using the debugger Open the file(s) you want to debug. You can set breakpoints or use the `debugger` keyword. Now, *with a test file open*, from the *Debug* menu run the *Debug AVA test file* configuration. ### Debugging precompiled tests If you compile your test files into a different directory, and run the tests *from* that directory, the above configuration won't work. Assuming the names of your test files are unique you could try the following configuration instead. This assumes the compile output is written to the `build` directory. Adjust as appropriate: ```json { "type": "node", "request": "launch", "name": "Debug AVA test file", "program": "${workspaceFolder}/node_modules/ava/entrypoints/cli.js", "args": [ "build/**/${fileBasenameNoExtension}.*" ], "outputCapture": "std", "console": "integratedTerminal", // optional "skipFiles": [ "/**/*.js" ] } ``` ### Using Yarn PnP (Plug'n'Play) As [Yarn's PnP (Plug'n'Play)](https://yarnpkg.com/features/pnp) installation strategy does not produce a `node_modules` folder, the `ava` binary must be called using `yarn run`: ```json { "type": "node", "request": "launch", "name": "Debug AVA test file", "runtimeExecutable": "yarn", "runtimeArgs": ["run", "ava"], "args": ["${file}"], "outputCapture": "std", "console": "integratedTerminal", // optional "skipFiles": [ "/**/*.js" ] } ``` ## Serial debugging By default AVA runs tests concurrently. This may complicate debugging. Instead make sure AVA runs only one test at a time. *Note that, if your tests aren't properly isolated, certain test failures may not appear when running the tests serially.* If you use the debug terminal make sure to invoke AVA with `npx ava --serial`. Or, if you're using a launch configuration, add the `--serial` argument: ```json { "type": "node", "request": "launch", "name": "Debug AVA test file", "program": "${workspaceFolder}/node_modules/ava/entrypoints/cli.js", "args": [ "--serial", "${file}" ], "outputCapture": "std", "console": "integratedTerminal", // optional "skipFiles": [ "/**/*.js" ] } ``` --- ### Recipes/Debugging With Webstorm # Debugging tests with WebStorm Translations: [Français](https://github.com/avajs/ava-docs/blob/main/fr_FR/docs/recipes/debugging-with-webstorm.md) **This recipe is outdated.** --- Starting with version 2016.2, [WebStorm](https://www.jetbrains.com/webstorm/) and other JetBrains IDEs (IntelliJ IDEA Ultimate, PHPStorm, PyCharm Professional, and RubyMine with installed Node.js plugin) allow you to debug AVA tests. ## Setup using Node.js Add a new *Node.js Run/Debug configuration*: select `Edit Configurations...` from the dropdown list on the top right, then click `+` and select *Node.js*. In the `JavaScript file` field specify the path to AVA in the project's `node_modules` folder: `node_modules/.bin/ava` on macOS and Linux or `node_modules/.bin/ava.cmd` on Windows. In the `Application parameters` pass the CLI flags you're using and the test files you would like to debug, for example `--verbose test.js`. In the `Node parameters`, pass the `--inspect-brk` flag to enable the Node inspector. Save the configuration. ## Setup using npm Execute `npx @ava/init` in your project directory to add AVA to your `package.json`. Your `package.json` will look something like this: ```json { "name": "awesome-package", "scripts": { "test": "ava" }, "devDependencies": { "ava": "^1.0.0" } } ``` Add a new *npm Run/Debug configuration*: select `Edit Configurations...` from the dropdown list on the top right, then click `+` and select *npm*. Use the following configuration parameters: - `package.json`: Path to your project's `package.json` file - `Command`: `test` Your IDE will then execute `npm run test` and thus call `node_modules/.bin/ava` and the AVA-configuration you have specified in your package.json. In the `Node parameters`, pass `--inspect-brk`. Don't forget to select a Node.js interpreter. Save the configuration. ## Debug Set breakpoints in the code. Hit the green `Debug` button next to the list of configurations on the top right. The *Debug tool window* will appear. Once the breakpoint is hit, you can evaluate variables and step through the code. When debugging multiple test files, you can switch between the processes using the dropdown in the Frames pane. --- ### Recipes/Endpoint Testing # Endpoint testing Translations: [Español](https://github.com/avajs/ava-docs/blob/main/es_ES/docs/recipes/endpoint-testing.md), [Français](https://github.com/avajs/ava-docs/blob/main/fr_FR/docs/recipes/endpoint-testing.md), [Italiano](https://github.com/avajs/ava-docs/blob/main/it_IT/docs/recipes/endpoint-testing.md), [日本語](https://github.com/avajs/ava-docs/blob/main/ja_JP/docs/recipes/endpoint-testing.md), [Português](https://github.com/avajs/ava-docs/blob/main/pt_BR/docs/recipes/endpoint-testing.md), [Русский](https://github.com/avajs/ava-docs/blob/main/ru_RU/docs/recipes/endpoint-testing.md), [简体中文](https://github.com/avajs/ava-docs/blob/main/zh_CN/docs/recipes/endpoint-testing.md) [](https://stackblitz.com/github/avajs/ava/tree/main/examples/endpoint-testing?file=test.js&terminal=test&view=editor) AVA doesn't have a built-in method for testing endpoints, but you can use any HTTP client of your choosing, for example [`ky`](https://github.com/sindresorhus/ky). You'll also need to start an HTTP server, preferably on a unique port so that you can run tests in parallel. For that we recommend [`async-listen`](https://github.com/vercel/async-listen). Since tests run concurrently, it's best to create a fresh server instance at least for each test file, but perhaps even for each test. This can be accomplished with `test.before()` and `test.beforeEach()` hooks and `t.context`. If you start your server using a `test.before()` hook you should make sure to execute your tests serially. Check out the example below: ```js import {createServer} from 'node:http'; import {listen} from 'async-listen'; import test from 'ava'; import ky, {HTTPError} from 'ky'; import app from './app.js'; test.before(async t => { t.context.server = createServer(app); t.context.prefixUrl = await listen(t.context.server); }); test.after.always(t => { t.context.server.close(); }); test.serial('get /user', async t => { const {email} = await ky('user', {prefixUrl: t.context.prefixUrl}).json(); t.is(email, 'ava@rocks.com'); }); test.serial('404', async t => { await t.throwsAsync( ky('password', {prefixUrl: t.context.prefixUrl}), {message: /Request failed with status code 404 Not Found/, instanceOf: HTTPError}, ); }); ``` Other libraries you may find useful: - [`supertest`](https://github.com/visionmedia/supertest) - [`get-port`](https://github.com/sindresorhus/get-port) --- ### Recipes/Endpoint Testing With Mongoose # Endpoint testing with Mongoose Translations: [Français](https://github.com/avajs/ava-docs/blob/main/fr_FR/docs/recipes/endpoint-testing-with-mongoose.md) This recipe shows you how to test your endpoints with AVA and Mongoose, assuming you use Express as your framework. ## Setup This recipe uses the following libraries: 1. [`mongodb-memory-server`](https://github.com/nodkz/mongodb-memory-server) (A MongoDB in-memory Server) 2. [SuperTest](https://github.com/visionmedia/supertest) (An endpoint testing library) 3. [Mongoose](https://mongoosejs.com) Install the first two libraries by running the following code: ```console $ npm install --save-dev mongodb-memory-server supertest ``` You should have Mongoose installed already. If not, run the following code to install it: (Note: You need at least Mongoose v4.11.3) ```console $ npm install mongoose ``` ## Prerequisites You'll need a server file and a Mongoose model. See the [`server.js`](https://github.com/zellwk/ava-mdb-test/blob/master/server.js) and [`models/User.js`](https://github.com/zellwk/ava-mdb-test/blob/master/models/User.js) examples. Note that `server.js` does not start the app. Instead this must be done by SuperTest, so that the app endpoints can be tested. If you're using Express for your application, make sure you have a startup file that imports `app` and calls `app.listen()`. ## Your test file First, include the libraries you need: ```js // Libraries required for testing import test from 'ava'; import request from 'supertest'; import {MongoMemoryServer} from 'mongodb-memory-server'; import mongoose from 'mongoose'; // Your server and models import app from '../server'; import User from '../models/User'; ``` Next start the in-memory MongoDB instance and connect to Mongoose: ```js // Create connection to Mongoose before tests are run test.before(async t => { // First start MongoDB instance t.context.mongod = await MongoMemoryServer.create(); // And connect await mongoose.connect(t.context.mongod.getUri()); }); ``` When you run your first test, MongoDB downloads the latest MongoDB binaries. The download is ~70MB so this may take a minute. You'll want to populate your database with dummy data. Here's an example: ```js test.beforeEach(async () => { const user = new User({ email: 'one@example.com', name: 'One' }); await user.save(); }); ``` Dummy data should be cleared after each test: ```js test.afterEach.always(() => User.remove()); ``` Now you can use SuperTest to send off a request for your app endpoint. Use AVA for your assertions: ```js // Note that the tests are run serially. See below as to why. test.serial('litmus get user', async t => { const {app} = t.context; const res = await request(app) .get('/litmus') .send({email: 'one@example.com'}); t.is(res.status, 200); t.is(res.body.name, 'One'); }); test.serial('litmus create user', async t => { const {app} = t.context; const res = await request(app) .post('/litmus') .send({ email: 'new@example.com', name: 'New name' }); t.is(res.status, 200); t.is(res.body.name, 'New name'); // Verify that user is created in DB const newUser = await User.findOne({email: 'new@example.com'}); t.is(newUser.name, 'New name'); }); ``` Finally disconnect from and stop MongoDB when all tests are done: ```js test.after.always(async t => { await mongoose.disconnect(); await t.context.mongod.stop(); }); ``` And you're done! ## Reusing the configuration across files You may choose to extract the code for the `test.before`, `test.beforeEach`, `test.afterEach.always` and `test.after.always` hooks into a separate file. Have a look at https://github.com/zellwk/ava-mdb-test for an example. ## Using `test.serial` instead of `test` Your tests likely change the database. Using `test()` means they run concurrently, which may cause one test to affect another. Instead if you use `test.serial()` then the tests will run one at a time. You can then clean up your database between test runs, making the tests more predictable. You could run tests concurrently if you create separate Mongoose connections for each test. This is harder to set up, though. More information can be found [here](https://github.com/nodkz/mongodb-memory-server#several-mongoose-connections-simultaneously). --- ### Recipes/Isolated Mongodb Integration Tests # Isolated MongoDB integration tests Translations: [Français](https://github.com/avajs/ava-docs/blob/main/fr_FR/docs/recipes/isolated-mongodb-integration-tests.md) > How to run disposable MongoDB databases in your AVA tests with per-test isolation. This uses [`MongoMem`](https://github.com/CImrie/mongomem), which allows you to quickly run a temporary MongoDB server locally. It uses temporary file storage which is destroyed when the server stops. ## Install MongoDB in-memory Server (MongoMem) In the root directory of your app, run: ```console $ npm install --save-dev mongomem ``` ## Using MongoMem In your test file, import the module, and run the server. **Make sure to run the server at the start of your file, outside of any test cases.** ```js import test from 'ava'; import {MongoDBServer} from 'mongomem'; test.before('start server', async t => { await MongoDBServer.start(); }) test('some feature', async t => { const connectionString = await MongoDBServer.getConnectionString(); // connectionString === 'mongodb://localhost:27017/3411fd12-b5d6-4860-854c-5bbdb011cb93' // Use `connectionString` to connect to the database with a client of your choice. See below for usage with Mongoose. }); ``` ## Cleaning Up After you have run your tests, you should include a `test.after.always()` method to clean up the MongoDB server. This will remove any temporary files the server used while running. This is normally cleaned up by your operating system, but it is good practise to do it manually. ```js test.after.always('cleanup', t => { MongoDBServer.tearDown(); // Cleans up temporary file storage }); ``` ## Debugging If the server does not seem to start, you can set the `MongoDBServer.debug = true;` option before you call `MongoDBServer.start()`. This will allow the MongoDB server to print connection or file permission errors when it's starting. It checks and picks an available port to run the server on, so errors are likely to be related to file permissions. ## Extra: Setup and use in Mongoose [Mongoose](https://mongoosejs.com) is a robust Object-Document-Mapper (ODM) for MongoDB. Refer to its documentation to get started with Mongoose. To use Mongoose effectively with AVA, check out the [Mongoose integration docs](endpoint-testing-with-mongoose.md). --- ### Recipes/Passing Arguments To Your Test Files # Passing arguments to your test files Translations: [Français](https://github.com/avajs/ava-docs/blob/main/fr_FR/docs/recipes/passing-arguments-to-your-test-files.md) You can pass command line arguments to your test files. Use the `--` argument terminator to separate AVA's arguments from your own: ```js // test.js import test from 'ava'; test('argv', t => { t.deepEqual(process.argv.slice(2), ['--hello', 'world']); }); ``` ```console $ npx ava -- --hello world ``` You need two `--` argument terminators if you're invoking AVA through an `npm test` script: ```json { "scripts": { "test": "ava" } } ``` ```console $ npm test -- -- --hello world ``` --- ### Recipes/Puppeteer # Testing web apps using Puppeteer Translations: [Français](https://github.com/avajs/ava-docs/blob/main/fr_FR/docs/recipes/puppeteer.md) ## Dependencies - [Puppeteer](https://github.com/GoogleChrome/puppeteer): `npm install --save-dev puppeteer` ## Setup The first step is setting up a helper to configure the environment: `./test/_withPage.js` ```js import puppeteer from 'puppeteer'; export default async (t, run) => { const browser = await puppeteer.launch(); const page = await browser.newPage(); try { await run(t, page); } finally { await page.close(); await browser.close(); } }; ``` ## Usage example `./test/main.js` ```js import test from 'ava'; import withPage from './_withPage.js'; const url = 'https://google.com'; test('page title should contain "Google"', withPage, async (t, page) => { await page.goto(url); t.true((await page.title()).includes('Google')); }); test('page should contain an element with `#hplogo` selector', withPage, async (t, page) => { await page.goto(url); t.not(await page.$('#hplogo'), null); }); test('search form should match the snapshot', withPage, async (t, page) => { await page.goto(url); const innerHTML = await page.evaluate(form => form.innerHTML, await page.$('#searchform')); t.snapshot(innerHTML); }); ``` --- ### Recipes/Shared Workers # Extending AVA using shared workers Shared workers are a powerful AVA feature. A program can be loaded in a [worker thread](https://nodejs.org/docs/latest/api/worker_threads.html) in AVA's main process and then communicate with code running in the test workers. This enables your tests to better utilize shared resources during a test run, as well as providing opportunities to set up these resources before tests start (or clean them up after). When you use watch mode, shared workers remain loaded across runs. ## Available plugins * [`@ava/get-port`](https://github.com/avajs/get-port) works like [`get-port`](https://github.com/sindresorhus/get-port), but ensures the port is locked across all test files. * [`@ava/cooperate`](https://github.com/avajs/cooperate) implements locking and value reservation functionality. ## Writing plugins You can write your own plugins. They can be high-level, like [`@ava/get-port`](https://github.com/avajs/get-port) which is built using [`@ava/cooperate`](https://github.com/avajs/cooperate), or low-level like [`@ava/cooperate`](https://github.com/avajs/cooperate) itself. Here we'll discuss building low-level plugins. ### Registering a shared worker Plugins are registered inside test workers. They'll provide the path for the shared worker, which AVA will load in a [worker thread](https://nodejs.org/docs/latest/api/worker_threads.html) in its main process. For each unique path one worker thread is started. Plugins communicate with their shared worker using a *protocol*. Protocols are versioned independently from AVA itself. This allows us to make improvements without breaking existing plugins. Protocols are only removed in major AVA releases. Plugins can be compatible with multiple protocols. AVA will select the best protocol it supports. If AVA does not support any of the specified protocols it'll throw an error. The selected protocol is available on the returned worker object. ```js import {registerSharedWorker} from 'ava/plugin'; const shared = registerSharedWorker({ filename: path.resolve(__dirname, 'worker.js'), supportedProtocols: ['ava-4'] }); ``` Within a test process you can only register one worker for each `filename`. Filenames are compared as-is, without normalization. If you call `registerSharedWorker()` a second time, the same worker instance is returned. If for some reason you want to load the same file as multiple different workers, you can append a unique hash to the end of the filename: ```js import crypto from 'crypto'; import {registerSharedWorker} from 'ava/plugin'; const key = Math.random() > 0.5 ? 'worker-a' : 'worker-b'; const shared = registerSharedWorker({ filename: new URL( `file:${path.resolve( __dirname, 'worker.js' )}#${encodeURIComponent(key)}` ), initialData: {workerKey: key}, supportedProtocols: ['ava-4'] }); ``` This works because the `filename` parameter accepts [URL](https://nodejs.org/api/url.html) objects, meaning you could use a query component for the key instead if you wanted. You can supply a `teardown()` function which will be called after all tests have finished. If you call `registerSharedWorker()` multiple times then the `teardown()` function will be invoked for each registration, even though you only got one worker instance. The most recently registered `teardown()` function is called first, and so forth. `teardown()` functions execute sequentially. ```js const worker = registerSharedWorker({ filename: path.resolve(__dirname, 'worker.js'), supportedProtocols: ['ava-4'], teardown () { // Perform any clean-up within the test process itself. } }); ``` You can also provide some data passed to the shared worker when it is loaded. Of course, it is only loaded once, so this is only useful in limited circumstances: ```js const shared = registerSharedWorker({ filename: path.resolve(__dirname, 'worker.js'), initialData: {hello: 'world'}, supportedProtocols: ['ava-4'] }); ``` On this `shared` object, `protocol` is set to the selected protocol. Since the shared worker is loaded asynchronously, `available` provides a promise that fulfils when the shared worker first becomes available. `currentlyAvailable` reflects whether the worker is, well, currently available. There are two more methods available on the `shared` object, which we'll get to soon. #### Initializing the shared worker AVA loads the shared worker (as identified through the `filename` option) in a worker thread. This must be an ES module file with a default export. The filename must be an absolute path using the `file:` protocol or a `URL` instance. The default export must be a factory method. Like when calling `registerSharedWorker()`, it must negotiate a protocol: ```js export default ({negotiateProtocol}) => { const main = negotiateProtocol(['ava-4']); } ``` On this `main` object, `protocol` is set to the selected protocol. `initialData` holds the data provided when the worker was first registered. When you're done initializing the shared worker you must call `main.ready()`. This makes the worker available in test workers. You can call `main.ready()` asynchronously. Any errors thrown by the factory method will crash the worker thread and make the worker unavailable in test workers. The same goes for unhandled rejections. The factory method may return a promise. ### Communicating between test workers and the shared worker AVA's low-level shared worker infrastructure is primarily about communication. You can send messages from test workers to the shared worker, and the other way around. Higher-level logic can be implemented on top of this message passing infrastructure. Message data is serialized using the [V8 Serialization API](https://nodejs.org/docs/latest-v12.x/api/v8.html#v8_serialization_api). Please read up on some [important limitations](https://nodejs.org/docs/latest-v12.x/api/worker_threads.html#worker_threads_port_postmessage_value_transferlist). In the shared worker you can subscribe to messages from test workers: ```js export default async ({negotiateProtocol}) => { const main = negotiateProtocol(['ava-4']).ready(); for await (const message of main.subscribe()) { // … } } ``` Messages have IDs that are unique for the main AVA process. Across AVA runs you may see the same ID. Access the ID using the `id` property. Access message data using the `data` property. You can reply to a received message by calling `reply()`. This publishes a message to the test process the message originated from. You can then subscribe to replies to *that* message using `replies()`. To illustrate this here's a "game" of Marco Polo: ```js export default ({negotiateProtocol}) => { const main = negotiateProtocol(['ava-4']).ready(); play(main.subscribe()); }; const play = async (messages) => { for await (const message of messages) { if (message.data === 'Marco') { const response = message.reply('Polo'); play(response.replies()); } } } ``` (Of course this sets up many reply listeners which is rather inefficient.) You can also broadcast messages to all connected test workers: ```js export default async ({negotiateProtocol}) => { const main = negotiateProtocol(['ava-4']).ready(); for await (const message of main.subscribe()) { if (message.data === 'Bingo!') { main.broadcast('Bingo!'); } } } ``` Like with `reply()`, `broadcast()` returns a published message which can receive replies. Call `replies()` to get an asynchronous iterator for reply messages. Each received message has a `testWorker` property to represent the test worker that the message originated from. All messages from the same test worker have the same value for this property. These test workers have a unique ID (which, like message IDs, is unique for the main process). Access it using the `id` property. The path of the test file is available through the `file` property. Use `publish()` to send messages directly to the test worker, and `subscribe()` to receive messages from the test worker. This works the same as `main.subscribe()`. Of course you don't need to wait for a message *from* a test worker to access this object. Use `main.testWorkers()` to get an asynchronous iterator which produces each newly connected test worker: ```js export default async ({negotiateProtocol}) => { const main = negotiateProtocol(['ava-4']).ready(); for await (const testWorker of main.testWorkers()) { main.broadcast(`New test file: ${testWorker.file}`); } } ``` Within test workers, once the shared worker is available, you can publish messages: ```js shared.publish('Marco'); ``` Or subscribe to messages: ```js (async () => { for await (const message of shared.subscribe()) { if (message.data === 'Polo') { message.reply('Marco'); } } })(); ``` `publish()` throws when the shared worker is not yet available. `publish()` and `subscribe()` throw when the worker has crashed. Message IDs are available and are unique for the main AVA process. Messages are always produced in their own turn of the event loop. This means you can use `async`/`await` to process a previous message or subscribe to replies and you'll be guaranteed to receive them. ### Cleaning up resources Test workers come and go while the shared worker remains. It's therefore important to clean up resources. Messages are subscribed to using async iterators. These return when the test worker exits. You can register teardown functions to be run when the test worker exits: ```js export default async ({negotiateProtocol}) => { const main = negotiateProtocol(['ava-4']).ready(); for await (const testWorker of main.testWorkers()) { testWorker.teardown(() => { // Bye bye… }); } } ``` The most recently registered function is called first, and so forth. Functions execute sequentially. More interestingly, a wrapped teardown function is returned so that you can call it manually. AVA still ensures the function only runs once. ```js export default ({negotiateProtocol}) => { const main = negotiateProtocol(['ava-4']).ready(); for await (const worker of testWorkers) { counters.set(worker, 0); const teardown = worker.teardown(() => { counters.delete(worker); }); waitForTen(worker.subscribe(), teardown); } } const counters = new WeakMap(); const waitForTen = async (messages, teardown) => { for await (const {testWorker} of messages) { const count = counters.get(testWorker) + 1; if (count === 10) { teardown(); } else { counters.set(testWorker, count); } } }; ``` ## Now it's your turn We're pretty excited about this feature! But we need more real-world experience in building AVA plugins before we can make it generally available. Please give feedback and build plugins. We'd be more than happy to promote them. Not sure what to build? Previously folks have expressed a desire for mutexes, managing Puppeteer instances, starting (database) servers and so forth. We could also extend the shared worker implementation in AVA itself. Perhaps so you can run code before a new test run, even with watch mode. Or so you can initialize a shared worker based on the AVA configuration, not when a test file runs. Please [comment here](https://github.com/avajs/ava/discussions/2703) with ideas, questions and feedback. --- ### Recipes/Splitting Tests Ci # Splitting tests in CI AVA automatically detects whether your CI environment supports parallel builds using [ci-parallel-vars](https://www.npmjs.com/package/ci-parallel-vars). When parallel builds support is detected, AVA sorts the all detected test files by name, and splits them into chunks. Each CI machine is assigned a chunk (subset) of the tests, and then each chunk is run in parallel. To disable this feature, set `utilizeParallelBuilds` to `false` in your [AVA configuration](/docs/06-configuration.md#options). To better distribute the tests across the machines, you can configure a custom comparator function: **`ava.config.js`:** ```js import fs from 'node:fs'; // Assuming 'test-data.json' structure is: // { // 'tests/test1.js': { order: 1 }, // 'tests/test2.js': { order: 0 } // } const testData = JSON.parse(fs.readFileSync('test-data.json', 'utf8')); export default { sortTestFiles: (file1, file2) => testData[file1].order - testData[file2].order, }; ``` ## Splitting tests on GitHub Actions Although GitHub Actions doesn't support parallel builds out-of-the-box with AVA, you can configure it manually by using a matrix: **`.github/workflows/test.yml`:** ```yml on: push jobs: test: strategy: # Don't cancel test runs if one fails fail-fast: false # Run 4 jobs in parallel, each executing a subset of all tests matrix: node_index: [0, 1, 2, 3] total_nodes: [4] runs-on: ubuntu-latest steps: # Check out code and perform setup steps # ... - name: Test run: npx ava env: CI_NODE_INDEX: ${{ matrix.node_index }} CI_NODE_TOTAL: ${{ matrix.total_nodes }} ``` --- ### Recipes/Test Setup # Test setup Translations: [Français](https://github.com/avajs/ava-docs/blob/main/fr_FR/docs/recipes/test-setup.md) Tests can be set up using the `beforeEach()` hook. Often though you could use a plain setup function instead. This recipe helps you decide what's best for your use case. # The `beforeEach()` hook versus setup functions The `beforeEach()` hook has some downsides. For example, you cannot turn it off for specific tests, nor can you apply it to specific tests. As an alternative, you can use simple functions. This allows you to use multiple setup functions for different setup requirements and call different parts of setup from different tests. You can even have setup functions with parameters so tests can customize their own setup. Let's say you have a function that interacts with the file system. Perhaps you run a few tests using `mock-fs`, and then a few that use the real file system and a temporary directory. Or you have a setup function that you run with valid data for some tests and invalid data for other tests, all within the same test file. You could do all these things using plain setup functions, but there are tradeoffs: |`beforeEach()`| Setup functions |---|--- | ⛔️   used for all tests| ✅   can change or skip depending on test | ⛔️   more overhead for beginners, "some magic"| ✅   easier for beginners, "no magic" | ✅   built-in support for observables| ⛔️   must use promises for asynchronous behavior | ✅   failure has friendly output| ⛔️   errors are attributed to the test | ✅   corresponding `afterEach` and `afterEach.always` for cleanup| ⛔️   cannot easily clean up ## Complex test setup In this example, we have both a `beforeEach()` hook, and then more modifications within each test. ```js test.beforeEach(t => { setupConditionA(t); setupConditionB(t); setupConditionC(t); }); test('first scenario', t => { tweakSomething(t); const someCondition = t.context.thingUnderTest(); t.true(someCondition); }); test('second scenario', t => { tweakSomethingElse(t); const someOtherCondition = t.context.thingUnderTest(); t.true(someOtherCondition); }); ``` If too many variables need changing for each test, consider omitting the `beforeEach()` hook and performing setup steps within the tests themselves. ```js test('first scenario', t => { setupConditionA(t); setupConditionB(t, {/* options */}); setupConditionC(t); const someCondition = t.context.thingUnderTest(); t.true(someCondition); }); // In this test, setupConditionB() is never called. test('second scenario', t => { setupConditionA(t); setupConditionC(t); const someOtherCondition = t.context.thingUnderTest(); t.true(someOtherCondition); }); ``` You can use [`t.teardown()`](../02-execution-context.md#tteardownfn) to register a teardown function which will run after the test has finished (regardless of whether it's passed or failed). ## A practical example ```js test.beforeEach(t => { t.context = { authenticator: new Authenticator(), credentials: new Credentials('admin', 's3cr3t') }; }); test('authenticating with valid credentials', async t => { const isValid = t.context.authenticator.authenticate(t.context.credentials); t.true(await isValid); }); test('authenticating with an invalid username', async t => { t.context.credentials.username = 'bad_username'; const isValid = t.context.authenticator.authenticate(t.context.credentials); t.false(await isValid); }); test('authenticating with an invalid password', async t => { t.context.credentials.password = 'bad_password'; const isValid = t.context.authenticator.authenticate(t.context.credentials); t.false(await isValid); }); ``` The same tests, now using setup functions, would look like the following. ```js function setup({username = 'admin', password = 's3cr3t'} = {}) { return { authenticator: new Authenticator(), credentials: new Credentials(username, password) }; } test('authenticating with valid credentials', async t => { const {authenticator, credentials} = setup(); const isValid = authenticator.authenticate(credentials); t.true(await isValid); }); test('authenticating with an invalid username', async t => { const {authenticator, credentials} = setup({username: 'bad_username'}); const isValid = authenticator.authenticate(credentials); t.false(await isValid); }); test('authenticating with an invalid password', async t => { const {authenticator, credentials} = setup({password: 'bad_password'}); const isValid = authenticator.authenticate(credentials); t.false(await isValid); }); ``` ## Combining hooks and setup functions Of course `beforeEach()` and plain setup functions can be used together: ```js test.beforeEach(t => { t.context = setupAllTests(); }); test('first scenario', t => { firstSetup(t); const someCondition = t.context.thingUnderTest(); t.true(someCondition); }); ``` --- ### Recipes/Testing With Selenium Webdriverjs # Setting up AVA with Selenium WebDriverJS This recipe shows you how to use the Selenium WebDriverJS (official JavaScript implementation) with AVA to test web apps. ## Setup This recipe uses the following packages: 1. [selenium-webdriver](https://www.npmjs.com/package/selenium-webdriver) 2. [chromedriver](https://www.npmjs.com/package/chromedriver) Install them with: ```console $ npm install selenium-webdriver chromedriver ``` As part of this recipe, we will use Selenium to verify web searches on [Bing](https://www.bing.com) and [Google](https://www.google.com). Create the following files: - `./test/bingtest.js` - `./test/googletest.js` In both files, let's first include the packages: ```js import test from 'ava'; import {Builder, By, Key, until} from 'selenium-webdriver'; import 'chromedriver'; ``` In the `bingtest.js` file, add the following code, which tests whether searching for `webdriver` on Bing, returns results. ```js test('Bing Search', async t => { const keyword = 'webdriver'; const driver = new Builder().forBrowser('chrome').build(); await driver.get('https://www.bing.com'); await driver.findElement(By.name('q')).sendKeys(keyword + Key.ENTER); await driver.wait(until.titleIs(keyword + ' - Bing')); t.true((await driver.findElements(By.css('#b_content #b_results li'))).length > 0); await driver.close(); }); ``` In the `googletest.js` file, instead of a single test, lets add two tests, one each for the terms 'webdriver' and 'avajs'. Since we would like to initialize the webdriver before each test, we use the [`beforeEach` and `afterEach`](../01-writing-tests.md#before--after-hooks) hooks to setup and teardown the driver respectively. Using these hooks, helps reduce the amount of code we would write in each `test()`. ```js test.beforeEach(async t => { t.context.driver = new Builder().forBrowser('chrome').build(); await t.context.driver.get('https://www.google.com'); }); test.afterEach('cleanup', async t => { await t.context.driver.close(); }); ``` Now lets add the test code: ```js async function searchGoogle(driver, keyword) { await driver.findElement(By.name('q')).sendKeys(keyword + Key.ENTER); await driver.wait(until.titleIs(`${keyword} - Google Search`)); } test('Google Search for avajs', async t => { const {driver} = t.context; await searchGoogle(driver, 'avajs'); t.true((await driver.findElement(By.id('resultStats')).getText()).includes('results')); }); test('Google Search for webdriver', async t => { const {driver} = t.context; await searchGoogle(driver, 'webdriver'); t.true((await driver.findElement(By.id('resultStats')).getText()).includes('results')); }); ``` You're now ready to run the tests. The output should look something like this: ```console npx ava DevTools listening on ws://127.0.0.1:49720/devtools/browser/9ebf4394-447b-4916-91cc-692d06d88896 DevTools listening on ws://127.0.0.1:49756/devtools/browser/6e19d9fe-4de6-40a3-b120-17067b3125ca DevTools listening on ws://127.0.0.1:49757/devtools/browser/ac12c2da-eeed-40d8-9b23-4d2103ec8fac bingtest » Bing Search (7.2s) googletest » Google Search for avajs (2.5s) googletest » Google Search for webdriver (3.3s) 3 tests passed ``` --- ### Recipes/Typescript # TypeScript Translations: [Español](https://github.com/avajs/ava-docs/blob/main/es_ES/docs/recipes/typescript.md), [Français](https://github.com/avajs/ava-docs/blob/main/fr_FR/docs/recipes/typescript.md), [Italiano](https://github.com/avajs/ava-docs/blob/main/it_IT/docs/recipes/typescript.md), [Русский](https://github.com/avajs/ava-docs/blob/main/ru_RU/docs/recipes/typescript.md), [简体中文](https://github.com/avajs/ava-docs/blob/main/zh_CN/docs/recipes/typescript.md) AVA comes bundled with a TypeScript definition file. This allows developers to leverage TypeScript for writing tests. This guide assumes you've already set up TypeScript for your project. Note that AVA's definition expects at least version 5.9. ## Enabling AVA's support for TypeScript test files Broadly speaking, there are two ways to run tests written in TypeScript: 1. Build first, then test against the build output 2. Configure loaders which build test files as they're loaded **The first option is the most reliable since it doesn't rely on experimental Node.js features.** You can use our [`@ava/typescript`] package, which is designed to work for projects that precompile TypeScript using the `tsc` command. Please see [`@ava/typescript`] for setup instructions. **This package also sets up the various TypeScript file extensions for you.** **You can use loaders, but you're largely on your own. [Please post questions to our Discussions forum if you're stuck](https://github.com/avajs/ava/discussions/categories/q-a).** > [!NOTE] > Custom loaders changed with the release of Node.js 20. This recipe assumes your Node.js version is equal to or higher than the following. For older versions, please see a [previous commit](https://github.com/avajs/ava/blob/aae39b20ba3ef80e5bedb1e5882432a3cd7c44eb/docs/recipes/typescript.md). > > | Node.js Major Version | Minimum Version | > | --------------------- | --------------- | > | 18 | 18.18.0 | > | 20 | 20.8.0 | > | 21 | 21.0.0 | There are two components to a setup like this: 1. [Make sure AVA recognizes the extensions of your TypeScript files](../06-configuration.md#configuring-module-formats) 2. Install the loader [through `nodeArguments`](../06-configuration.md#node-arguments) [`tsimp`](https://github.com/tapjs/tsimp) may be the best loader available. The setup, assuming your TypeScript config outputs ES modules, would look like this: `package.json`: ```json "ava": { "extensions": ["ts"], "nodeArguments": [ "--import=tsimp" ] } ``` ## Writing tests [](https://stackblitz.com/github/avajs/ava/tree/main/examples/typescript-basic?file=source%2Ftest.ts&terminal=test&view=editor) Create a `test.ts` file using ESM syntax. ```ts import test from 'ava'; const fn = () => 'foo'; test('fn() returns foo', t => { t.is(fn(), 'foo'); }); ``` ## Using [macros](../01-writing-tests.md#reusing-test-logic-through-macros) Macros can receive additional arguments. AVA can infer these to ensure you're using the macro correctly: ```ts import test, {ExecutionContext} from 'ava'; const hasLength = (t: ExecutionContext, input: string, expected: number) => { t.is(input.length, expected); }; test('bar has length 3', hasLength, 'bar', 3); ``` However if you use the `test.macro()` helper you get much better type inference: ```ts import test from 'ava'; const macro = test.macro((t, input: string, expected: number) => { t.is(eval(input), expected); }); test('title', macro, '3 * 3', 9); ``` Or with a title function: ```ts import test from 'ava'; const macro = test.macro({ exec(t, input: string, expected: number) { t.is(eval(input), expected); }, title(providedTitle = '', input, expected) { return `${providedTitle} ${input} = ${expected}`.trim(); } }); test(macro, '2 + 2', 4); test(macro, '2 * 3', 6); test('providedTitle', macro, '3 * 3', 9); ``` ## Typing [`t.context`](../01-writing-tests.md#test-context) [](https://stackblitz.com/github/avajs/ava/tree/main/examples/typescript-context?file=source%2Ftest.ts&terminal=test&view=editor) By default, the type of `t.context` will be the empty object (`{}`). AVA exposes an interface `TestFn` which you can use to apply your own type to `t.context`. This can help you catch errors at compile-time: ```ts import anyTest, {TestFn} from 'ava'; const test = anyTest as TestFn<{foo: string}>; test.beforeEach(t => { t.context = {foo: 'bar'}; }); test.beforeEach(t => { t.context.foo = 123; // error: Type '123' is not assignable to type 'string' }); test.serial.failing('very long chains are properly typed', t => { t.context.fooo = 'a value'; // error: Property 'fooo' does not exist on type '' }); test('an actual test', t => { t.deepEqual(t.context.foo.map(c => c), ['b', 'a', 'r']); // error: Property 'map' does not exist on type 'string' }); ``` Note that, despite the type cast above, when executing `t.context` is an empty object unless it's assigned. ## Typing `throws` assertions The `t.throws()` and `t.throwsAsync()` assertions are typed to always return an `Error`. You can customize the error class using generics: ```ts import test from 'ava'; class CustomError extends Error { parent: Error constructor(parent) { super(parent.message); this.parent = parent; } } function myFunc() { throw new CustomError(new TypeError('🙈')); }; test('throws', t => { const err = t.throws(myFunc); t.is(err.parent.name, 'TypeError'); }); test('throwsAsync', async t => { const err = await t.throwsAsync(async () => myFunc()); t.is(err.parent.name, 'TypeError'); }); ``` [`@ava/typescript`]: https://github.com/avajs/typescript --- ### Recipes/Vue # Testing Vue.js components Translations: [Français](https://github.com/avajs/ava-docs/blob/main/fr_FR/docs/recipes/vue.md) ## Dependencies - [Require extension hooks](https://github.com/jackmellis/require-extension-hooks): - `npm i --save-dev require-extension-hooks require-extension-hooks-vue require-extension-hooks-babel@beta` - [jsdom-global](https://github.com/rstacruz/jsdom-global/blob/master/README.md) - `npm i --save-dev jsdom jsdom-global` - Optional: [babel-plugin-webpack-alias-7](https://github.com/shortminds/babel-plugin-webpack-alias-7) if you want to use [webpack aliases](https://webpack.js.org/configuration/resolve/#resolve-alias) or use them in your source files - `npm i --save-dev babel-plugin-webpack-alias-7` ## Status This recipe relied on legacy `require()` hooks. AVA is ESM-only, so this setup is no longer supported. Precompile your Vue components before running AVA, or use tooling that provides an ESM loader for `.vue` files. ## Coverage reporting Follow the [coverage reporting recipe](code-coverage.md), additionally adding the `.vue` extension to the `c8` config to instrument `.vue` files. ```json { "c8": { "extension": [ ".js", ".vue" ] } } ``` --- ### Recipes/Watch Mode # Watch mode Translations: [Français](https://github.com/avajs/ava-docs/blob/main/fr_FR/docs/recipes/watch-mode.md), [Italiano](https://github.com/avajs/ava-docs/blob/main/it_IT/docs/recipes/watch-mode.md), [Русский](https://github.com/avajs/ava-docs/blob/main/ru_RU/docs/recipes/watch-mode.md), [简体中文](https://github.com/avajs/ava-docs/blob/main/zh_CN/docs/recipes/watch-mode.md) AVA comes with an intelligent watch mode. It watches for files to change and runs just those tests that are affected. ## Running tests with watch mode enabled You can enable watch mode using the `--watch` or `-w` flags: ```console $ npx ava --watch ``` Please note that integrated debugging and the TAP reporter are unavailable when using watch mode. ## Requirements AVA uses `fs.watch()`. Support for `recursive` mode is required. Note that this has only become available on Linux since Node.js 20. [Other caveats apply](https://nodejs.org/api/fs.html#caveats), for example this won't work well on network filesystems and Docker host mounts. ## Ignoring changes By default AVA watches for changes to all files, except for those with `.snap.md` or `.tsbuildinfo` extensions, `ava.config.*` and files in [certain directories](https://github.com/novemberborn/ignore-by-default/blob/master/index.js) as provided by the [`ignore-by-default`] package. You can configure additional patterns for files to ignore in the [`ava` section of your `package.json`, or `ava.config.*` file][config], using the `ignoreChanges` key within the `watchMode` object: ```js export default { watchMode: { ignoreChanges: ['coverage'], }, }; ``` If your tests write to disk they may trigger the watcher to rerun your tests. Configuring additional ignore patterns helps avoid this. ### Filter tests while watching You may also filter tests while watching by using the CLI. For example, after running ```console npx ava --watch ``` You will see a prompt like this: ```console Type `g` followed by enter to filter test files by a glob pattern Type `m` followed by enter to filter tests by their title Type `r` followed by enter to rerun tests Type `u` followed by enter to update snapshots in selected tests > ``` So, to run only tests numbered like - foo23434 - foo4343 - foo93823 You can type `m` and press enter, then type `foo*` and press enter. This will then run all tests that match that glob. Afterwards you can use the `r` command to run the matched tests again, or `a` command to run **all** tests. ## Dependency tracking AVA tracks which source files your test files depend on. If you change such a dependency only the test file that depends on it will be rerun. AVA will rerun all tests if it cannot determine which test file depends on the changed source file. Dependency tracking works for static `import` syntax, as supported by [@vercel/nft](https://github.com/vercel/nft). `import()` is supported but dynamic paths such as `import(myVariable)` are not. Files accessed using the `fs` module are not tracked. ## Watch mode and CI If you run AVA in your CI with watch mode, the execution will exit with an error (`Error : Watch mode is not available in CI, as it prevents AVA from terminating.`). AVA will not run with the `--watch` (`-w`) option in CI, because CI processes should terminate, and with the `--watch` option, AVA will never terminate. ## Manually rerunning all tests You can quickly rerun all tests by typing r on the console, followed by Enter. ## Updating snapshots You can update failing snapshots by typing u on the console, followed by Enter. ## Debugging Sometimes watch mode does something surprising like rerunning all tests when you thought only a single test would be run. To see its reasoning you can enable a debug mode. This will work best with the verbose reporter: ```console $ DEBUG=ava:watcher npx ava --watch ``` [Install Troubleshooting]: https://github.com/paulmillr/chokidar#install-troubleshooting [`ignore-by-default`]: https://github.com/novemberborn/ignore-by-default [`.only` modifier]: ../01-writing-tests.md#running-specific-tests [config]: ../06-configuration.md --- ### Recipes/When To Use Plan # When to use `t.plan()` Translations: [Español](https://github.com/avajs/ava-docs/blob/main/es_ES/docs/recipes/when-to-use-plan.md), [Français](https://github.com/avajs/ava-docs/blob/main/fr_FR/docs/recipes/when-to-use-plan.md), [Italiano](https://github.com/avajs/ava-docs/blob/main/it_IT/docs/recipes/when-to-use-plan.md), [日本語](https://github.com/avajs/ava-docs/blob/main/ja_JP/docs/recipes/when-to-use-plan.md), [Português](https://github.com/avajs/ava-docs/blob/main/pt_BR/docs/recipes/when-to-use-plan.md), [Русский](https://github.com/avajs/ava-docs/blob/main/ru_RU/docs/recipes/when-to-use-plan.md), [简体中文](https://github.com/avajs/ava-docs/blob/main/zh_CN/docs/recipes/when-to-use-plan.md) One major difference between AVA and [`tap`](https://github.com/tapjs/node-tap)/[`tape`](https://github.com/substack/tape) is the behavior of `t.plan()`. In AVA, `t.plan()` is only used to assert that the expected number of assertions are called; it does not auto-end the test. ## Poor uses of `t.plan()` Many users transitioning from `tap`/`tape` are accustomed to using `t.plan()` prolifically in every test. However, in AVA, we don't consider that to be a "best practice". Instead, we believe `t.plan()` should only be used in situations where it provides some value. ### Sync tests with no branching `t.plan()` is unnecessary in most sync tests. ```js test('simple sums', t => { // BAD: there is no branching here - t.plan() is pointless t.plan(2); t.is(1 + 1, 2); t.is(2 + 2, 4); }); ``` `t.plan()` does not provide any value here, and creates an extra chore if you ever decide to add or remove assertions. ### Promises that are expected to resolve ```js test('gives foo', t => { t.plan(1); return somePromise().then(result => { t.is(result, 'foo'); }); }); ``` At a glance, this tests appears to make good use of `t.plan()` since an async promise handler is involved. However there are several problems with the test: 1. `t.plan()` is presumably used here to protect against the possibility that `somePromise()` might be rejected; But returning a rejected promise would fail the test anyways. 2. It would be better to take advantage of `async`/`await`: ```js test('gives foo', async t => { t.is(await somePromise(), 'foo'); }); ``` ### Promises with a `.catch()` block ```js test('rejects with foo', t => { t.plan(2); return shouldRejectWithFoo().catch(reason => { t.is(reason.message, 'Hello'); t.is(reason.foo, 'bar'); }); }); ``` Here, the use of `t.plan()` seeks to ensure that the code inside the `catch` block is executed. Instead, you should take advantage of `t.throwsAsync` and `async`/`await`, as this leads to flatter code that is easier to reason about: ```js test('rejects with foo', async t => { const reason = await t.throwsAsync(shouldRejectWithFoo()); t.is(reason.message, 'Hello'); t.is(reason.foo, 'bar'); }); ``` ### Ensuring a catch statement happens ```js test('throws', t => { t.plan(2); try { shouldThrow(); } catch (err) { t.is(err.message, 'Hello'); t.is(err.foo, 'bar'); } }); ``` As stated in the previous example, using the `t.throws()` assertion with `async`/`await` is a better choice. ## Good uses of `t.plan()` `t.plan()` provides value in the following cases. ### Tests with branching statements In most cases, it's a bad idea to use any complex branching inside your tests. A notable exception is for tests that are auto-generated (perhaps from a JSON document). Below `t.plan()` is used to ensure the correctness of the JSON input: ```js import fs from 'node:fs'; import path from 'node:path'; const testData = JSON.parse(fs.readFileSync(new URL('./fixtures/test-definitions.json', import.meta.url))); for (const testDefinition of testData) { test('foo or bar', t => { const result = functionUnderTest(testDefinition.input); // testDefinition should have an expectation for `foo` or `bar` but not both t.plan(1); if (testDefinition.foo) { t.is(result.foo, testDefinition.foo); } if (testDefinition.bar) { t.is(result.bar, testDefinition.foo); } }); } ``` ## Conclusion `t.plan()` has plenty of valid uses, but it should not be used indiscriminately. A good rule of thumb is to use it any time your *test* does not have straightforward, easily reasoned about, code flow. Tests with assertions inside callbacks, `if`/`then` statements, `for`/`while` loops, and (in some cases) `try`/`catch` blocks, are all good candidates for `t.plan()`. --- ### 01 Writing Tests # Writing tests Translations: [Français](https://github.com/avajs/ava-docs/blob/main/fr_FR/docs/01-writing-tests.md) Tests are run concurrently. You can specify synchronous and asynchronous tests. Tests are considered synchronous unless you return a promise or an [observable](https://github.com/zenparsing/zen-observable). You must define all tests synchronously. They can't be defined inside `setTimeout`, `setImmediate`, etc. AVA tries to run test files with their current working directory set to the directory that contains your `package.json` file. ## Test isolation By default each test file is run in a new worker thread. You can fall back running in separate processes (see `workerThreads` [CLI option](./06-configuration.md#options)). AVA will set `process.env.NODE_ENV` to `test`, unless the `NODE_ENV` environment variable has been set. This is useful if the code you're testing has test defaults (for example when picking what database to connect to). It may cause your code or its dependencies to behave differently though. Note that `'NODE_ENV' in process.env` will always be `true`. ## Declaring tests To declare a test you call the `test` function you imported from AVA. Provide the required title and implementation function. Titles must be unique within each test file. The function will be called when your test is run. It's passed an [execution object](./02-execution-context.md) as its first argument. ```js import test from 'ava'; test('my passing test', t => { t.pass(); }); ``` ## Running tests serially Tests are run concurrently by default, however, sometimes you have to write tests that cannot run concurrently. In these rare cases you can use the `.serial` modifier. It will force those tests to run serially *before* the concurrent ones. ```js test.serial('passes serially', t => { t.pass(); }); ``` Note that this only applies to tests within a particular test file. AVA will still run multiple tests files at the same time unless you pass the [`--serial` CLI flag](./05-command-line.md). You can use the `.serial` modifier with all tests, hooks and even `.todo()`, but it's only available on the `test` function. ## Promise support Tests may return a promise. AVA will wait for the promise to resolve before ending the test. If the promise rejects the test will fail. ```js test('resolves with unicorn', t => { return somePromise().then(result => { t.is(result, 'unicorn'); }); }); ``` ## Async function support AVA comes with built-in support for [async functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function). ```js test(async function (t) { const value = await promiseFn(); t.true(value); }); // Async arrow function test('promises the truth', async t => { const value = await promiseFn(); t.true(value); }); ``` ## Observable support AVA comes with built-in support for [observables](https://github.com/zenparsing/es-observable). If you return an observable from a test, AVA will automatically consume it to completion before ending the test. ```js test('handles observables', t => { t.plan(3); return Observable.of(1, 2, 3, 4, 5, 6) .filter(n => { // Only even numbers return n % 2 === 0; }) .map(() => t.pass()); }); ``` ## Running specific tests During development it can be helpful to only run a few specific tests. This can be accomplished using the `.only` modifier: ```js test('will not be run', t => { t.fail(); }); test.only('will be run', t => { t.pass(); }); ``` You can use the `.only` modifier with all tests. It cannot be used with hooks or `.todo()`. *Note:* The `.only` modifier applies to the test file it's defined in, so if you run multiple test files, tests in other files will still run. If you want to only run the `test.only` test, provide just that test file to AVA. ## Skipping tests Sometimes failing tests can be hard to fix. You can tell AVA to temporarily skip these tests using the `.skip` modifier. They'll still be shown in the output (as having been skipped) but are never run. ```js test.skip('will not be run', t => { t.fail(); }); ``` You must specify the implementation function. You can use the `.skip` modifier with all tests and hooks, but not with `.todo()`. You can not apply further modifiers to `.skip`. If the test is likely to be failing for a while, use `.failing()` instead. ## Skipping tests conditionally Use `test.skipIf()` to skip a test based on a runtime condition. `test.runIf()` is the inverse: the test only runs when the condition is true. ```js test.skipIf(process.platform === 'win32')('not on Windows', t => { t.pass(); }); test.runIf(process.platform === 'linux')('Linux only', t => { t.pass(); }); ``` These work with other modifiers like `.serial` and `.failing`: ```js test.serial.skipIf(process.platform === 'win32')('serial, not on Windows', t => { t.pass(); }); test.failing.skipIf(process.platform === 'win32')('expected failure, not on Windows', t => { t.fail(); }); ``` ## Test placeholders ("todo") You can use the `.todo` modifier when you're planning to write a test. Like skipped tests these placeholders are shown in the output. They only require a title; you cannot specify the implementation function. ```js test.todo('will think about writing this later'); ``` You can signal that you need to write a serial test: ```js test.serial.todo('will think about writing this later'); ``` ## Failing tests You can use the `.failing` modifier to document issues with your code that need to be fixed. Failing tests are run just like normal ones, but they are expected to fail, and will not break your build when they do. If a test marked as failing actually passes, it will be reported as an error and fail the build with a helpful message instructing you to remove the `.failing` modifier. This allows you to merge `.failing` tests before a fix is implemented without breaking CI. This is a great way to recognize good bug report PR's with a commit credit, even if the reporter is unable to actually fix the problem. ```js // See: github.com/user/repo/issues/1234 test.failing('demonstrate some bug', t => { t.fail(); // Test will count as passed }); ``` ## Before & after hooks AVA lets you register hooks that are run before and after your tests. This allows you to run setup and/or teardown code. `test.before()` registers a hook to be run before the first test in your test file. Similarly `test.after()` registers a hook to be run after the last test. Use `test.after.always()` to register a hook that will **always** run once your tests and other hooks complete. `.always()` hooks run regardless of whether there were earlier failures, so they are ideal for cleanup tasks. Note however that uncaught exceptions, unhandled rejections or timeouts will crash your tests, possibly preventing `.always()` hooks from running. `test.beforeEach()` registers a hook to be run before each test in your test file. Similarly `test.afterEach()` registers a hook to be run after each test. Use `test.afterEach.always()` to register an after hook that is called even if other test hooks, or the test itself, fail. If a test is skipped with the `.skip` modifier, the respective `.beforeEach()`, `.afterEach()` and `.afterEach.always()` hooks are not run. Likewise, if all tests in a test file are skipped `.before()`, `.after()` and `.after.always()` hooks for the file are not run. *You may not need to use `.afterEach.always()` hooks to clean up after a test.* You can use [`t.teardown()`](./02-execution-context.md#tteardownfn) to undo side-effects *within* a particular test. Or use [`registerCompletionHandler()`](./08-common-pitfalls.md#timeouts-because-a-file-failed-to-exit) to run cleanup code after AVA has completed its work. Like `test()` these methods take an optional title and an implementation function. The title is shown if your hook fails to execute. The implementation is called with an [execution object](./02-execution-context.md). You can use assertions in your hooks. You can also pass a [macro function](#reusing-test-logic-through-macros) and additional arguments. `.before()` hooks execute before `.beforeEach()` hooks. `.afterEach()` hooks execute before `.after()` hooks. Within their category the hooks execute in the order they were defined. By default hooks execute concurrently, but you can use `test.serial` to ensure only that single hook is run at a time. Unlike with tests, serial hooks are *not* run before other hooks: ```js test.before(t => { // This runs before all tests }); test.before(t => { // This runs concurrently with the above }); test.serial.before(t => { // This runs after the above }); test.serial.before(t => { // This too runs after the above, and before tests }); test.after('cleanup', t => { // This runs after all tests }); test.after.always('guaranteed cleanup', t => { // This will always run, regardless of earlier failures }); test.beforeEach(t => { // This runs before each test }); test.afterEach(t => { // This runs after each test }); test.afterEach.always(t => { // This runs after each test and other test hooks, even if they failed }); test('title', t => { // Regular test }); ``` Hooks can be synchronous or asynchronous, just like tests. To make a hook asynchronous return a promise or observable, or use an async function. ```js test.before(async t => { await promiseFn(); }); test.after(t => { return new Promise(/* ... */); }); ``` Keep in mind that the `.beforeEach()` and `.afterEach()` hooks run just before and after a test is run, and that by default tests run concurrently. This means each multiple `.beforeEach()` hooks may run concurrently. Using `test.serial.beforeEach()` does not change this. If you need to set up global state for each test (like spying on `console.log` [for example](https://github.com/avajs/ava/issues/560)), you'll need to make sure the tests themselves are [run serially](#running-tests-serially). Remember that AVA runs each test file in its own process. You may not have to clean up global state in a `.after()`-hook since that's only called right before the process exits. ## Test context Hooks can share context with the test: ```js test.beforeEach(t => { t.context.data = generateUniqueData(); }); test('context data is foo', t => { t.is(t.context.data + 'bar', 'foobar'); }); ``` If `.before()` hooks treat `t.context` as an object, a shallow copy is made and passed to `.beforeEach()` hooks and / or tests. Other types of values are passed as-is. The `.after()` and `.after.always()` hooks receive the original context value. For `.beforeEach()`, `.afterEach()` and `.afterEach.always()` hooks the context is *not* shared between different tests, allowing you to set up data such that it will not leak to other tests. By default `t.context` is an object but you can reassign it: ```js test.before(t => { t.context = 'unicorn'; }); test('context is unicorn', t => { t.is(t.context, 'unicorn'); }); ``` ## Retrieving test metadata Access data about the currently loaded test file run by reading `test.meta`. Available properties: * `file`: path to the test file, as a file URL string * `snapshotDirectory`: directory where snapshots are stored, as a file URL string ```js import test from 'ava'; console.log('Test file currently being run:', test.meta.file); ``` ## Reusing test logic through macros [](https://stackblitz.com/github/avajs/ava/tree/main/examples/macros?file=test.js&terminal=test&view=editor) Additional arguments passed to the test declaration will be passed to the test implementation. This is useful for creating reusable test macros. You _could_ use plain functions: ```js function macro(t, input, expected) { t.is(eval(input), expected); } test('2 + 2 = 4', macro, '2 + 2', 4); test('2 * 3 = 6', macro, '2 * 3', 6); ``` However the preferred approach is to use the `test.macro()` helper: ```js import test from 'ava'; const macro = test.macro((t, input, expected) => { t.is(eval(input), expected); }); test('title', macro, '3 * 3', 9); ``` Or with a title function: ```js import test from 'ava'; const macro = test.macro({ exec(t, input, expected) { t.is(eval(input), expected); }, title(providedTitle = '', input, expected) { return `${providedTitle} ${input} = ${expected}`.trim(); }, }); test(macro, '2 + 2', 4); test(macro, '2 * 3', 6); test('providedTitle', macro, '3 * 3', 9); ``` The `providedTitle` argument defaults to `undefined` if the user does not supply a string title. This means you can use a parameter assignment to set the default value. The example above uses the empty string as the default. --- ### 02 Execution Context # Execution Context (`t` argument) Translations: [Français](https://github.com/avajs/ava-docs/blob/main/fr_FR/docs/02-execution-context.md) Each test or hook is called with an execution context. By convention it's named `t`. ```js import test from 'ava'; test('my passing test', t => { t.pass(); }); ``` Each test or hook receives a different object. It contains the [assertions](./03-assertions.md) as well as the methods and properties listed below. ## `t.title` The test title. ## `t.context` Contains shared state from hooks. ## `t.passed` When used in `test.afterEach()` or `test.afterEach.always()` hooks this tells you whether the test has passed. When used in a test itself (including teardown functions) this remains `true` until an assertion fails, the test has ended with an error, or a teardown function caused an error. This value has no meaning in other hooks. ## `t.log(...values)` Log values contextually alongside the test result instead of immediately printing them to `stdout`. Behaves somewhat like `console.log`, but without support for placeholder tokens. ## `t.plan(count)` Plan how many assertions there are in the test. The test will fail if the actual assertion count doesn't match the number of planned assertions. See [assertion planning](./03-assertions.md#assertion-planning). ## `t.teardown(fn)` Registers the `fn` function to be run after the test has finished. You can register multiple functions. They'll run in reverse order, so the last registered function is run first. You can use asynchronous functions: only one will run at a time. You cannot perform assertions using the `t` object or register additional functions from inside `fn`. You cannot use `t.teardown()` in hooks either. ## `t.timeout(ms)` Set a timeout for the test, in milliseconds. The test will fail if this timeout is exceeded. The timeout is reset each time an assertion is made. Use `t.timeout.clear()` to clear the timeout and restore the default behavior. --- ### 03 Assertions # Assertions Translations: [Français](https://github.com/avajs/ava-docs/blob/main/fr_FR/docs/03-assertions.md) Assertions are mixed into the [execution object](./02-execution-context.md) provided to each test implementation: ```js test('unicorns are truthy', t => { t.truthy('unicorn'); // Assertion }); ``` Assertions are bound to their test so you can assign them to a variable or pass them around: ```js test('unicorns are truthy', t => { const truthy = t.truthy; truthy('unicorn'); }); ``` If multiple assertion failures are encountered within a single test, AVA will only display the *first* one. Assertions return `true` if they've passed and throw otherwise. Catching this error does not cause the test to pass. The error value is undocumented. If you use TypeScript you can use some assertions as type guards. Note that the "throws" assertions return the error that was thrown (provided the assertion passed). ## Assertion planning Assertion plans ensure tests only pass when a specific number of assertions have been executed. They'll help you catch cases where tests exit too early. They'll also cause tests to fail if too many assertions are executed, which can be useful if you have assertions inside callbacks or loops. If you do not specify an assertion plan, your test will still fail if no assertions are executed. Set the `failWithoutAssertions` option to `false` in AVA's [`package.json` configuration](./06-configuration.md) to disable this behavior. Note that, unlike [`tap`](https://www.npmjs.com/package/tap) and [`tape`](https://www.npmjs.com/package/tape), AVA does *not* automatically end a test when the planned assertion count is reached. These examples will result in a passed test: ```js test('resolves with 3', t => { t.plan(1); return Promise.resolve(3).then(n => { t.is(n, 3); }); }); ``` These won't: ```js test('loops twice', t => { t.plan(2); for (let i = 0; i < 3; i++) { t.true(i < 3); } }); // Fails, 3 assertions are executed which is too many test('invokes callback synchronously', t => { t.plan(1); someAsyncFunction(() => { t.pass(); }); }); // Fails, the test ends synchronously before the assertion is executed ``` ## Skipping assertions Any assertion can be skipped using the `skip` modifier. Skipped assertions are still counted, so there is no need to change your planned assertion count. ```js test('skip assertion', t => { t.plan(2); t.is.skip(foo(), 5); // No need to change your plan count when skipping t.is(1, 1); }); ``` ## Custom assertions You can use any assertion library instead of or in addition to the built-in one, provided it throws exceptions when the assertion fails. This won't give you as nice an experience as you'd get with the [built-in assertions](#built-in-assertions) though, and you won't be able to use the [assertion planning](#assertion-planning) ([see #25](https://github.com/avajs/ava/issues/25)). You'll have to configure AVA to not fail tests if no assertions are executed, because AVA can't tell if custom assertions pass. Set the `failWithoutAssertions` option to `false` in AVA's [`package.json` configuration](./06-configuration.md). ```js import assert from 'assert'; test('custom assertion', t => { assert(true); }); ``` ## Built-in assertions ### `.pass(message?)` Passing assertion. ### `.fail(message?)` Failing assertion. ### `.assert(actual, message?)` Asserts that `actual` is truthy. ### `.truthy(actual, message?)` Assert that `actual` is truthy. ### `.falsy(actual, message?)` Assert that `actual` is falsy. ### `.true(actual, message?)` Assert that `actual` is `true`. ### `.false(actual, message?)` Assert that `actual` is `false`. ### `.is(actual, expected, message?)` Assert that `actual` is the same as `expected`. This is based on [`Object.is()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is). ### `.not(actual, expected, message?)` Assert that `actual` is not the same as `expected`. This is based on [`Object.is()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is). ### `.deepEqual(actual, expected, message?)` Assert that `actual` is deeply equal to `expected`. See [Concordance](https://github.com/concordancejs/concordance) for details. ### `.notDeepEqual(actual, expected, message?)` Assert that `actual` is not deeply equal to `expected`. The inverse of `.deepEqual()`. ### `.like(actual, selector, message?)` Assert that `actual` is like `selector`. This is a variant of `.deepEqual()`, however `selector` does not need to have the same enumerable properties as `actual` does. Instead AVA derives a *comparable* value from `actual`, recursively based on the enumerable shape of `selector`. This value is then compared to `selector` using `.deepEqual()`. Any values in `selector` that are not arrays or regular objects should be deeply equal to the corresponding values in `actual`. In the following example, the `map` property of `actual` must be deeply equal to that of `selector`. However `nested.qux` is ignored, because it's not in `selector`. ```js t.like({ map: new Map([['foo', 'bar']]), nested: { baz: 'thud', qux: 'quux' } }, { map: new Map([['foo', 'bar']]), nested: { baz: 'thud', } }) ``` You can also use arrays, but note that any indices in `actual` that are not in `selector` are ignored: ```js t.like([1, 2, 3, 4], [1, , 3]) ``` ### `.throws(fn, expectation?, message?)` Assert that an error is thrown. `fn` must be a function which should throw. By default, the thrown value *must* be an error. It is returned so you can run more assertions against it. `expectation` can be an object with one or more of the following properties: * `any`: a boolean, if `true` then the thrown value does not need to be an error. Defaults to `false` * `instanceOf`: a constructor, the thrown error must be an instance of * `is`: the thrown error must be strictly equal to `expectation.is` * `message`: the following types are valid: * *string* - it is compared against the thrown error's message * *regular expression* - it is matched against this message * *function* - it is passed the thrown error message and must return a boolean for whether the assertion passed * `name`: the expected `.name` value of the thrown error * `code`: the expected `.code` value of the thrown error `expectation` does not need to be specified. If you don't need it but do want to set an assertion message you have to specify `undefined`. Example: ```js const fn = () => { throw new TypeError('🦄'); }; test('throws', t => { const error = t.throws(() => { fn(); }, {instanceOf: TypeError}); t.is(error.message, '🦄'); }); ``` ### `.throwsAsync(thrower, expectation?, message?)` Assert that an error is thrown. `thrower` can be an async function which should throw, or a promise that should reject. This assertion must be awaited. By default, the thrown value *must* be an error. It is returned so you can run more assertions against it. `expectation` can be an object with one or more of the following properties: * `any`: a boolean, if `true` then the thrown value does not need to be an error. Defaults to `false` * `instanceOf`: a constructor, the thrown error must be an instance of * `is`: the thrown error must be strictly equal to `expectation.is` * `message`: the following types are valid: * *string* - it is compared against the thrown error's message * *regular expression* - it is matched against this message * *function* - it is passed the thrown error message and must return a boolean for whether the assertion passed * `name`: the expected `.name` value of the thrown error * `code`: the expected `.code` value of the thrown error `expectation` does not need to be specified. If you don't need it but do want to set an assertion message you have to specify `undefined`. Example: ```js test('throws', async t => { await t.throwsAsync(async () => { throw new TypeError('🦄'); }, {instanceOf: TypeError, message: '🦄'}); }); ``` ```js const promise = Promise.reject(new TypeError('🦄')); test('rejects', async t => { const error = await t.throwsAsync(promise); t.is(error.message, '🦄'); }); ``` ### `.notThrows(fn, message?)` Assert that no error is thrown. `fn` must be a function which shouldn't throw. ### `.notThrowsAsync(nonThrower, message?)` Assert that no error is thrown. `nonThrower` can be an async function which shouldn't throw, or a promise that should resolve. Like the `.throwsAsync()` assertion, you must wait for the assertion to complete: ```js test('resolves', async t => { await t.notThrowsAsync(promise); }); ``` ### `.regex(contents, regex, message?)` Assert that `contents` matches `regex`. ### `.notRegex(contents, regex, message?)` Assert that `contents` does not match `regex`. ### `.snapshot(expected, message?)` Compares the `expected` value with a previously recorded snapshot. Snapshots are stored for each test, so ensure you give your tests unique titles. ### `.try(title?, implementation | macro, ...args?)` `.try()` allows you to *try* assertions without causing the test to fail. The implementation function behaves the same as any other test function. You can even use macros. The first title argument is always optional. Additional arguments are passed to the implementation or macro function. `.try()` is an asynchronous function. You must `await` it. The result object has `commit()` and `discard()` methods. You must decide whether to commit or discard the result. If you commit a failed result, your test will fail. Calling `commit()` on a failed result will throw an error. You can check whether the attempt passed using the `passed` property. Any assertion errors are available through the `errors` property. The attempt title is available through the `title` property. Logs from `t.log()` are available through the `logs` property. You can choose to retain these logs as part of your test by passing `{retainLogs: true}` to the `commit()` and `discard()` methods. The implementation function receives its own [execution context](./02-execution-context.md), just like a test function. You must be careful to only perform assertions using the attempt's execution context. At least one assertion must pass for your attempt to pass. You may run multiple attempts concurrently, within a single test. However you can't use snapshots when you do so. Example: ```js const twoRandomIntegers = () => { const rnd = Math.round(Math.random() * 100); const x = rnd % 10; const y = Math.floor(rnd / 10); return [x, y]; }; test('flaky macro', async t => { const firstTry = await t.try((tt, a, b) => { tt.is(a, b); }, ...twoRandomIntegers()); if (firstTry.passed) { firstTry.commit(); return; } firstTry.discard(); t.log(firstTry.errors); const secondTry = await t.try((tt, a, b) => { tt.is(a, b); }, ...twoRandomIntegers()); secondTry.commit(); }); ``` --- ### 04 Snapshot Testing # Snapshot testing Translations: [Français](https://github.com/avajs/ava-docs/blob/main/fr_FR/docs/04-snapshot-testing.md) AVA supports snapshot testing, [as introduced by Jest](https://facebook.github.io/jest/docs/snapshot-testing.html), through its [Assertions](./03-assertions.md) interface. You can snapshot any value. Snapshots are stored alongside your test files. If your tests are in a `test` or `tests` folder the snapshots will be stored in a `snapshots` folder. If your tests are in a `__tests__` folder then they they'll be stored in a `__snapshots__` folder. Say you have `~/project/test/main.js` which contains snapshot assertions. AVA will create two files: * `~/project/test/snapshots/main.js.snap` * `~/project/test/snapshots/main.js.md` The first file contains the actual snapshot and is required for future comparisons. The second file contains your *snapshot report*. It's regenerated when you update your snapshots. If you commit it to source control you can diff it to see the changes to your snapshot. AVA will show why your snapshot assertion failed: You can then check your code. If the change was intentional you can use the `--update-snapshots` (or `-u`) flag to update the snapshots: ```console $ ava --update-snapshots ``` If you need to update snapshots for only a particular test, you can use `--update-snapshots` together with e.g. `--match` or `.only()` to select the test. You can specify a fixed location for storing the snapshot files in AVA's [`package.json` configuration](./06-configuration.md): **`package.json`:** ```json { "ava": { "snapshotDir": "custom-directory" } } ``` The snapshot files will be saved in a directory structure that mirrors that of your test files. If you are running AVA against precompiled test files, AVA will try and use source maps to determine the location of the original files. Snapshots will be stored next to these files, following the same rules as if AVA had executed the original files directly. This is great if you're writing your tests in TypeScript (see our [TypeScript recipe](./recipes/typescript.md)). --- ### 05 Command Line # CLI Translations: [Français](https://github.com/avajs/ava-docs/blob/main/fr_FR/docs/05-command-line.md) ``` /* Detailed source-code truncated for AI context efficiency. */ ``` AVA searches for test files using the following patterns: * `test.js` * `src/test.js` * `source/test.js` * `**/test-*.js` * `**/*.spec.js` * `**/*.test.js` * `**/test/**/*.js` * `**/tests/**/*.js` * `**/__tests__/**/*.js` Files inside `node_modules` are *always* ignored. So are files starting with `_` or inside of directories that start with a single `_`. Additionally, files matching these patterns are ignored by default, unless different patterns are configured: * `**/__tests__/**/__helper__/**/*` * `**/__tests__/**/__helpers__/**/*` * `**/__tests__/**/__fixture__/**/*` * `**/__tests__/**/__fixtures__/**/*` * `**/test/**/helper/**/*` * `**/test/**/helpers/**/*` * `**/test/**/fixture/**/*` * `**/test/**/fixtures/**/*` * `**/tests/**/helper/**/*` * `**/tests/**/helpers/**/*` * `**/tests/**/fixture/**/*` * `**/tests/**/fixtures/**/*` When using `npm test`, you can pass positional arguments directly `npm test test2.js`, but flags needs to be passed like `npm test -- --verbose`. ## Running tests with matching titles [](https://stackblitz.com/github/avajs/ava/tree/main/examples/matching-titles?file=test.js&terminal=test&view=editor) The `--match` flag allows you to run just the tests that have a matching title. This is achieved with simple wildcard patterns. Patterns are case insensitive. See [`matcher`](https://github.com/sindresorhus/matcher) for more details. Match titles ending with `foo`: ```console npx ava --match='*foo' ``` Match titles starting with `foo`: ```console npx ava --match='foo*' ``` Match titles containing `foo`: ```console npx ava --match='*foo*' ``` Match titles that are *exactly* `foo` (albeit case insensitively): ```console npx ava --match='foo' ``` Match titles not containing `foo`: ```console npx ava --match='!*foo*' ``` Match titles starting with `foo` and ending with `bar`: ```console npx ava --match='foo*bar' ``` Match titles starting with `foo` or ending with `bar`: ```console npx ava --match='foo*' --match='*bar' ``` Note that a match pattern takes precedence over the `.only` modifier. Only tests with an explicit title are matched. Tests without titles or whose title is derived from the implementation function will be skipped when `--match` is used. Here's what happens when you run AVA with a match pattern of `*oo*` and the following tests: ```js test('foo will run', t => { t.pass(); }); test('moo will also run', t => { t.pass(); }); test.only('boo will run but not exclusively', t => { t.pass(); }); ``` ## Running tests at specific line numbers [](https://stackblitz.com/github/avajs/ava/tree/main/examples/specific-line-numbers?file=test.js&terminal=test&view=editor) AVA lets you run tests exclusively by referring to their line numbers. Target a single line, a range of lines or both. You can select any line number of a test. The format is a comma-separated list of `[X|Y-Z]` where `X`, `Y` and `Z` are integers between `1` and the last line number of the file. This feature is only available from the command line. ### Running a single test To only run a particular test in a file, append the line number of the test to the path or pattern passed to AVA. Given the following test file: `test.js` ```js 1: test('unicorn', t => { 2: t.pass(); 3: }); 4: 5: test('rainbow', t => { 6: t.fail(); 7: }); ``` Running `npx ava test.js:2` for would run the `unicorn` test. In fact you could use any line number between `1` and `3`. ### Running multiple tests To run multiple tests, either target them one by one or select a range of line numbers. As line numbers are given per file, you can run multiple files with different line numbers for each file. If the same file is provided multiple times, line numbers are merged and only run once. ### Examples Single line numbers: ```console npx ava test.js:2,9 ``` Range: ```console npx ava test.js:4-7 ``` Mix of single line number and range: ```console npx ava test.js:4,9-12 ``` Different files: ```console npx ava test.js:3 test2.js:4,7-9 ``` When running a file with and without line numbers, line numbers take precedence. ## Resetting AVA's cache AVA maintains some temporary state. You can clear this state by running: ```console npx ava reset-cache ``` This deletes all files in the `node_modules/.cache/ava` directory. ## Reporters AVA uses a human readable reporter by default: ### TAP reporter [](https://stackblitz.com/github/avajs/ava/tree/main/examples/tap-reporter?file=test.js&terminal=test&view=editor) AVA supports the TAP format and thus is compatible with [any TAP reporter](https://github.com/sindresorhus/awesome-tap#reporters). Use the `--tap` flag to enable TAP output. ```console $ npx ava --tap | npx tap-nyan ``` Please note that the TAP reporter is unavailable when using [watch mode](./recipes/watch-mode.md). ## Node arguments The `--node-arguments` argument may be used to specify additional arguments for launching worker processes. These are combined with the `nodeArguments` configuration and any arguments passed to the `node` binary when starting AVA. **Only pass trusted values.** Specify the arguments as a single string: ```console npx ava --node-arguments="--throw-deprecation --zero-fill-buffers" ``` **Only pass trusted values.** --- ### 06 Configuration # Configuration Translations: [Français](https://github.com/avajs/ava-docs/blob/main/fr_FR/docs/06-configuration.md) All of the [CLI options][CLI] can be configured in the `ava` section of either your `package.json` file, or an `ava.config.*` file. This allows you to modify the default behavior of the `ava` command, so you don't have to repeatedly type the same options on the command prompt. To ignore files, prefix the pattern with an `!` (exclamation mark). **`package.json`:** ```json { "ava": { "files": [ "test/**/*", "!test/exclude-files-in-this-directory", "!**/exclude-files-with-this-name.*" ], "match": [ "*oo", "!foo" ], "concurrency": 5, "failFast": true, "failWithoutAssertions": false, "environmentVariables": { "MY_ENVIRONMENT_VARIABLE": "some value" }, "verbose": true, "require": [ "./my-helper-module.js" ], "nodeArguments": [ "--trace-deprecation", "--napi-modules" ] } } ``` Arguments passed to the CLI will always take precedence over the CLI options configured in `package.json`. ## Options - `files`: an array of glob patterns to select test files. Files with an underscore prefix are ignored. By default only selects files with `mjs` & `js` extensions, even if the pattern matches other files. Specify `extensions` to allow other file extensions - `watchMode`: See the [watch mode recipe for details](https://github.com/avajs/ava/blob/main/docs/recipes/watch-mode.md) - `match`: not typically useful in the `package.json` configuration, but equivalent to [specifying `--match` on the CLI](./05-command-line.md#running-tests-with-matching-titles) - `cache`: defaults to `true` to cache compiled files under `node_modules/.cache/ava`. If `false`, files are cached in a temporary directory instead - `concurrency`: max number of test files running at the same time (default: CPU cores) - `workerThreads`: use worker threads to run tests (enabled by default). If `false`, tests will run in child processes - `failFast`: stop running further tests once a test fails - `failWithoutAssertions`: if `false`, does not fail a test if it doesn't run [assertions](./03-assertions.md) - `environmentVariables`: specifies environment variables to be made available to the tests. The environment variables defined here override the ones from `process.env` - `serial`: if `true`, prevents parallel execution of tests within a file - `tap`: if `true`, enables the [TAP reporter](./05-command-line.md#tap-reporter) - `verbose`: if `true`, enables verbose output (though there currently non-verbose output is not supported) - `snapshotDir`: specifies a fixed location for storing snapshot files. Use this if your snapshots are ending up in the wrong location - `extensions`: extensions of test files. Setting this overrides the default `["mjs", "js"]` value, so make sure to include those extensions in the list. - `require`: [extra modules to load before test files](#requiring-extra-modules) - `timeout`: Timeouts in AVA behave differently than in other test frameworks. AVA resets a timer after each test, forcing tests to quit if no new test results were received within the specified timeout. This can be used to handle stalled tests. See our [timeout documentation](./07-test-timeouts.md) for more options. - `nodeArguments`: Configure Node.js arguments used to launch worker processes. - `sortTestFiles`: A comparator function to sort test files with. Available only when using a `ava.config.*` file. See an example use case [here](recipes/splitting-tests-ci.md). - `utilizeParallelBuilds`: If `false`, disable [parallel builds](/docs/recipes/splitting-tests-ci.md) (default: true) Note that providing files on the CLI overrides the `files` option. Provide the `typescript` option (and install [`@ava/typescript`](https://github.com/avajs/typescript) as an additional dependency) for AVA to run tests written in TypeScript. ## Using `ava.config.*` files Rather than specifying the configuration in the `package.json` file you can use an `ava.config.js` file. If your project does not already treat `.js` files as ES modules, use `ava.config.mjs` instead. To use these files: 1. Your `package.json` must not contain an `ava` property (or, if it does, it must be an empty object) 2. You must only have one `ava.config.*` file in any directory, so don't mix `ava.config.js` *and* `ava.config.mjs` files AVA searches your file system for `ava.config.*` files. First, when you run AVA, it finds the closest `package.json`. Starting in that directory it recursively checks the parent directories until it either reaches the file system root or encounters a `.git` file or directory. The first `ava.config.*` file found is selected. This allows you to use a single configuration file in a monorepo setup. ### `ava.config.js` Prefer `ava.config.js` when your project already treats `.js` files as ES modules, for example through `"type": "module"` in `package.json`. The default export can either be a plain object or a factory function which returns a plain object. You can export or return a promise for a plain object: ```js export default { require: ['./_my-test-helper.js'] }; ``` ```js export default function factory() { return { require: ['./_my-test-helper.js'] }; }; ``` The factory function is called with an object containing a `projectDir` property, which you could use to change the returned configuration: ```js export default ({projectDir}) => { if (projectDir === '/Users/username/projects/my-project') { return { // Config A }; } return { // Config B }; }; ``` ### `ava.config.mjs` Use `ava.config.mjs` when you need the filename itself to opt into ESM. The default export can either be a plain object or a factory function which returns a plain object. You can export or return a promise for a plain object: ```js export default { require: ['./_my-test-helper.js'] }; ``` ```js export default function factory() { return { require: ['./_my-test-helper.js'] }; }; ``` The factory function is called with an object containing a `projectDir` property, which you could use to change the returned configuration: ```js export default ({projectDir}) => { if (projectDir === '/Users/username/projects/my-project') { return { // Config A }; } return { // Config B }; }; ``` ## Alternative configuration files The [CLI] lets you specify a specific configuration file, using the `--config` flag. Prefer a `.js` file. Use `.mjs` only when your project does not already treat `.js` files as ESM. When the `--config` flag is set, the provided file will override all configuration from the `package.json` and `ava.config.js` or `ava.config.mjs` files. The configuration is not merged. You can use this to customize configuration for a specific test run. For instance, you may want to run unit tests separately from integration tests: `ava.config.js`: ```js export default { files: ['unit-tests/**/*'] }; ``` `integration-tests.config.js`: ```js import baseConfig from './ava.config.js'; export default { ...baseConfig, files: ['integration-tests/**/*'] }; ``` You can now run your unit tests through `npx ava` and the integration tests through `npx ava --config integration-tests.config.js`. ## Object printing depth By default, AVA prints nested objects to a depth of `3`. However, when debugging tests with deeply nested objects, it can be useful to print with more detail. This can be done by setting [`util.inspect.defaultOptions.depth`](https://nodejs.org/api/util.html#util_util_inspect_defaultoptions) to the desired depth, before the test is executed: ```js import util from 'util'; import test from 'ava'; util.inspect.defaultOptions.depth = 5; // Increase AVA's printing depth test('My test', t => { t.deepEqual(someDeeplyNestedObject, theExpectedValue); }); ``` AVA has a minimum depth of `3`. ## Experiments From time to time, AVA will implement experimental features. These may change or be removed at any time, not just when there's a new major version. You can opt in to such a feature by enabling it in the `nonSemVerExperiments` configuration. `ava.config.js`: ```js export default { nonSemVerExperiments: { feature: true } }; ``` ## Requiring extra modules Use the `require` configuration to load extra modules before test files are loaded. Relative paths are resolved against the project directory and can be loaded through `@ava/typescript`. Otherwise, modules are loaded from within the `node_modules` directory inside the project. You may specify a single value, or an array of values: `ava.config.js`: ```js export default { require: './_my-test-helper.js' } ``` ```js export default { require: ['./_my-test-helper.js'] } ``` If the module exports a function, it is called and awaited: `_my-test-helper.js`: ```js export default function () { // Additional setup } ``` You can provide arguments: `ava.config.js`: ```js export default { require: [ ['./_my-test-helper.js', 'my', 'arguments'] ] } ``` `_my-test-helper.js`: ```js export default function (first, second) { // 'my', 'arguments' // Additional setup } ``` Arguments are copied using the [structured clone algorithm](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm). This means `Map` values survive, but a `Buffer` will come out as a `Uint8Array`. You can load dependencies installed in your project: `ava.config.js`: ```js export default { require: '@babel/register' } ``` These may also export a function which is then invoked, and can receive arguments. ## Node arguments The `nodeArguments` configuration may be used to specify additional arguments for launching worker processes. These are combined with `--node-arguments` passed on the CLI and any arguments passed to the `node` binary when starting AVA. [CLI]: ./05-command-line.md ## Node arguments filter for worker threads In a config file only, `filterNodeArgumentsForWorkerThreads` may provide a function used for filtering `nodeArguments` sent to worker threads. This enables excluding arguments that throw if sent to a thread. The filter is ignored by worker processes. `ava.config.js`: ```js const processOnly = new Set([ '--allow-natives-syntax', '--expose-gc' ]); export default { filterNodeArgumentsForWorkerThreads: argument => !processOnly.has(argument) } ``` --- ### 07 Test Timeouts # Test timeouts Translations: [Français](https://github.com/avajs/ava-docs/blob/main/fr_FR/docs/07-test-timeouts.md) [](https://stackblitz.com/github/avajs/ava/tree/main/examples/timeouts?file=test.js&terminal=test&view=editor) Timeouts in AVA behave differently than in other test frameworks. AVA resets a timer after each test, forcing tests to quit if no new test results were received within the specified timeout. This can be used to handle stalled tests. This same mechanism is used to determine when a test file is preventing a clean exit. The default timeout is 10 seconds. You can configure timeouts using the `--timeout` [command line option](./05-command-line.md), or in the [configuration](./06-configuration.md). They can be set in a human-readable way: ```console npx ava --timeout=10s # 10 seconds npx ava --timeout=2m # 2 minutes npx ava --timeout=100 # 100 milliseconds ``` ### `t.timeout(ms, message?)` Timeouts can also be set individually for each test. These timeouts are reset each time an assertion is made. The test fails if it takes more than `ms` for an assertion to be made or the test to complete. ```js test('foo', t => { t.timeout(100); // 100 milliseconds // Write your assertions here }); ``` An optional message string can be provided. This can be useful if your test depends on some other setup that may not have been completed: ```js test('foo', t => { t.timeout(100, 'make sure database has started'); // 100 milliseconds // Write your assertions here }); ``` --- ### 08 Common Pitfalls # Common Pitfalls Translations: [Français](https://github.com/avajs/ava-docs/blob/main/fr_FR/docs/08-common-pitfalls.md) If you use [ESLint](https://eslint.org), you can install [eslint-plugin-ava](https://github.com/avajs/eslint-plugin-ava). It will help you use AVA correctly and avoid some common pitfalls. ## Error edge cases The `throws()` and `throwsAsync()` assertions use the Node.js built-in [`isNativeError()`](https://nodejs.org/api/util.html#utiltypesisnativeerrorvalue) to determine whether something is an error. This only recognizes actual instances of `Error` (and subclasses). Note that the following is not a native error: ```js const error = Object.create(Error.prototype); ``` This can be surprising, since `error instanceof Error` returns `true`. You can set `any: true` in the expectations to handle these values: ```js const error = Object.create(Error.prototype); t.throws(() => { throw error }, {any: true}); ``` ## AVA in Docker If you run AVA in Docker as part of your CI, you need to fix the appropriate environment variables. Specifically, adding `-e CI=true` in the `docker exec` command. See [#751](https://github.com/avajs/ava/issues/751). AVA uses [is-ci](https://github.com/watson/is-ci) to decide if it's in a CI environment or not using [these variables](https://github.com/watson/ci-info/blob/master/index.js). ## AVA and connected client limits You may be using a service that only allows a limited number of concurrent connections. For example, many database-as-a-service businesses offer a free plan with a limit on how many clients can be using it at the same time. AVA can hit those limits as it runs multiple processes, but well-written services should emit an error or throttle in those cases. If the one you're using doesn't, the tests will hang. By default, AVA will use as many processes as there are [logical cores](https://superuser.com/questions/1105654/logical-vs-physical-cpu-performance) on your machine. This is capped at two in a CI environment. Use the `concurrency` flag to limit the number of processes ran. For example, if your service plan allows 5 clients, you should run AVA with `concurrency=5` or less. ## Asynchronous operations You may be running an asynchronous operation inside a test and wondering why it's not finishing. If your asynchronous operation uses promises, you should return the promise: ```js test('fetches foo', t => { return fetch().then(data => { t.is(data, 'foo'); }); }); ``` Better yet, use `async` / `await`: ```js test('fetches foo', async t => { const data = await fetch(); t.is(data, 'foo'); }); ``` If you're using callbacks, promisify the callback function using something like [`util.promisify()`](https://nodejs.org/dist/latest/docs/api/util.html#util_util_promisify_original): ```js import {promisify} from 'util'; test('fetches foo', async t => { const data = await promisify(fetch)(); t.is(data, 'foo'); }); ``` ## Attributing uncaught exceptions to tests AVA [can't trace uncaught exceptions](https://github.com/avajs/ava/issues/214) back to the test that triggered them. Callback-taking functions may lead to uncaught exceptions that can then be hard to debug. Consider promisifying and using `async`/`await`, as in the above example. This should allow AVA to catch the exception and attribute it to the correct test. ## Node.js command line options, child processes and worker threads By default AVA runs test files in worker threads. However, not all Node.js command line options (those that end up in [`process.execArgv`](https://nodejs.org/api/process.html#processexecargv)) are compatible with worker threads. You may get an error like this: ``` Error [ERR_WORKER_INVALID_EXEC_ARGV]: Initiated Worker with invalid execArgv flags: --title ``` If possible don't specify the command line option when running AVA. Alternatively you could [disable worker threads in AVA](./06-configuration.md#options). ## Timeouts because a file failed to exit You may get a "Timed out while running tests" error because AVA failed to exit when running a particular file. AVA waits for Node.js to exit the worker thread or child process. If this takes too long, AVA counts it as a timeout. It is best practice to make sure your code exits cleanly. We've also seen occurrences where an explicit `process.exit()` call inside a worker thread could not be observed in AVA's main process. For these reasons we're not providing an option to disable this timeout behavior. However, it is possible to register a callback for when AVA has completed the test run without uncaught exceptions or unhandled rejections. From inside this callback you can do whatever you need to do, including calling `process.exit()`. Create a `_force-exit.js` file: ```js import process from 'node:process'; import { registerCompletionHandler } from 'ava'; registerCompletionHandler(() => { process.exit(); }); ``` Completion handlers are invoked in order of registration. Results are not awaited. Load it for all test files through AVA's `require` option: ```js export default { require: ['./_force-exit.js'], }; ``` ## Sharing variables between asynchronous tests By default AVA executes tests concurrently. This can cause problems if your tests are asynchronous and share variables. Take this contrived example: ```js import test from 'ava'; let count = 0; const incr = async () => { await true; count = count + 1; }; test.beforeEach('reset the count', () => { count = 0; }); test('increment once', async t => { await incr(); t.is(count, 1); }); test('increment twice', async t => { await incr(); await incr(); t.is(count, 2); }); ``` Concurrent tests allow for asynchronous tests to execute more quickly, but if they rely on shared state this may lead to unexpected test failures. If the shared state cannot be avoided, you can execute your tests serially: ```js import test from 'ava'; let count = 0; const incr = async () => { await true; count = count + 1; }; test.beforeEach('reset the count', () => { count = 0; }); test.serial('increment once', async t => { await incr(); t.is(count, 1); }); test.serial('increment twice', async t => { await incr(); await incr(); t.is(count, 2); }); ``` --- Is your problem not listed here? Submit a pull request or comment on [this issue](https://github.com/avajs/ava/issues/404). --- ### Support Statement # Supported Node.js versions Translations: [Français](https://github.com/avajs/ava-docs/blob/main/fr_FR/docs/support-statement.md) AVA supports the latest release of any major version that [is supported by Node.js itself](https://github.com/nodejs/Release#release-schedule). *Support* here means that we run our test suite under the given Node.js versions and will accept pull requests to fix any bugs (provided they're not known bugs in Node.js itself that will be fixed imminently). Consequently, *dropping support* means we'll remove those Node.js versions from our test matrix and will no longer accept specific pull requests to fix bugs under those versions. When we drop support for an LTS-covered major version we will bump AVA's major version number. We try to avoid *accidentally* dropping support for non-latest Node.js releases. If such breakage does occur we'll accept pull requests to restore functionality. We might decide to deprecate the offending AVA release and bump AVA's major version number instead. Whenever we bump AVA's major version number, we *will* explicitly drop support for non-latest Node.js releases. This ensures we can rely on backported APIs or the availability of newer V8 releases in later Node.js versions, either in AVA itself or one of our dependencies. We may drop support for a Node.js version, in a major-version-bumping-pre-release, if that new AVA version is expected to become stable around or after the end-of-life date of the Node.js version in question. Experimental features opted into through the `nonSemVerExperiments` configuration may be changed or removed at any time. --- ### Readme *[Please support our friend Vadim Demedes and the people in Ukraine.](https://stand-with-ukraine.pp.ua/)* --- # AVA logo AVA is a test runner for Node.js with a concise API, detailed error output, embrace of new language features and thread isolation that lets you develop with confidence 🚀 Watch this repository and follow the [Discussions](https://github.com/avajs/ava/discussions) for updates. Read our [contributing guide](.github/CONTRIBUTING.md) if you're looking to contribute (issues / PRs / etc). Translations: [Español](https://github.com/avajs/ava-docs/blob/main/es_ES/readme.md), [Français](https://github.com/avajs/ava-docs/blob/main/fr_FR/readme.md), [Italiano](https://github.com/avajs/ava-docs/blob/main/it_IT/readme.md), [日本語](https://github.com/avajs/ava-docs/blob/main/ja_JP/readme.md), [한국어](https://github.com/avajs/ava-docs/blob/main/ko_KR/readme.md), [Português](https://github.com/avajs/ava-docs/blob/main/pt_BR/readme.md), [Русский](https://github.com/avajs/ava-docs/blob/main/ru_RU/readme.md), [简体中文](https://github.com/avajs/ava-docs/blob/main/zh_CN/readme.md) ## Why AVA? - Minimal and fast - Simple test syntax - Runs tests concurrently - Enforces writing atomic tests - No implicit globals - Includes TypeScript definitions - [Magic assert](#magic-assert) - [Isolated environment for each test file](./docs/01-writing-tests.md#test-isolation) - [Promise support](./docs/01-writing-tests.md#promise-support) - [Async function support](./docs/01-writing-tests.md#async-function-support) - [Observable support](./docs/01-writing-tests.md#observable-support) - [Enhanced assertion messages](./docs/03-assertions.md#enhanced-assertion-messages) - [Automatic parallel test runs in CI](#parallel-runs-in-ci) - [TAP reporter](./docs/05-command-line.md#tap-reporter) ## Usage To install and set up AVA, run: ```console npm init ava ``` Your `package.json` will then look like this (exact version notwithstanding): ```json { "name": "awesome-package", "type": "module", "scripts": { "test": "ava" }, "devDependencies": { "ava": "^5.0.0" } } ``` Or if you prefer using Yarn: ```console yarn add ava --dev ``` Alternatively you can install `ava` manually: ```console npm install --save-dev ava ``` *Make sure to install AVA locally. AVA cannot be run globally.* Don't forget to configure the `test` script in your `package.json` as per above. ### Create your test file Create a file named `test.js` in the project root directory. _Note that AVA's documentation assumes you're using ES modules._ ```js import test from 'ava'; test('foo', t => { t.pass(); }); test('bar', async t => { const bar = Promise.resolve('bar'); t.is(await bar, 'bar'); }); ``` ### Running your tests ```console npm test ``` Or with `npx`: ```console npx ava ``` Run with the `--watch` flag to enable AVA's [watch mode](docs/recipes/watch-mode.md): ```console npx ava --watch ``` ## Supported Node.js versions AVA supports the latest release of any major version that [is supported by Node.js itself](https://github.com/nodejs/Release#release-schedule). Read more in our [support statement](docs/support-statement.md). ## Highlights ### Magic assert AVA adds code excerpts and clean diffs for actual and expected values. If values in the assertion are objects or arrays, only a diff is displayed, to remove the noise and focus on the problem. The diff is syntax-highlighted too! If you are comparing strings, both single and multi line, AVA displays a different kind of output, highlighting the added or missing characters. ### Clean stack traces AVA automatically removes unrelated lines in stack traces, allowing you to find the source of an error much faster, as seen above. ### Parallel runs in CI AVA automatically detects whether your CI environment supports parallel builds. Each build will run a subset of all test files, while still making sure all tests get executed. See the [`ci-parallel-vars`](https://www.npmjs.com/package/ci-parallel-vars) package for a list of supported CI environments. ## Documentation Please see the [files in the `docs` directory](./docs): * [Writing tests](./docs/01-writing-tests.md) * [Execution context](./docs/02-execution-context.md) * [Assertions](./docs/03-assertions.md) * [Snapshot testing](./docs/04-snapshot-testing.md) * [Command line (CLI)](./docs/05-command-line.md) * [Configuration](./docs/06-configuration.md) * [Test timeouts](./docs/07-test-timeouts.md) ### Common pitfalls We have a growing list of [common pitfalls](docs/08-common-pitfalls.md) you may experience while using AVA. If you encounter any issues you think are common, comment in [this issue](https://github.com/avajs/ava/issues/404). ### Recipes - [Test setup](docs/recipes/test-setup.md) - [TypeScript](docs/recipes/typescript.md) - [Shared workers](docs/recipes/shared-workers.md) - [Watch mode](docs/recipes/watch-mode.md) - [When to use `t.plan()`](docs/recipes/when-to-use-plan.md) - [Passing arguments to your test files](docs/recipes/passing-arguments-to-your-test-files.md) - [Splitting tests in CI](docs/recipes/splitting-tests-ci.md) - [Code coverage](docs/recipes/code-coverage.md) - [Endpoint testing](docs/recipes/endpoint-testing.md) - [Browser testing](docs/recipes/browser-testing.md) - [Testing Vue.js components](docs/recipes/vue.md) - [Debugging tests with Chrome DevTools](docs/recipes/debugging-with-chrome-devtools.md) - [Debugging tests with VSCode](docs/recipes/debugging-with-vscode.md) - [Debugging tests with WebStorm](docs/recipes/debugging-with-webstorm.md) - [Isolated MongoDB integration tests](docs/recipes/isolated-mongodb-integration-tests.md) - [Testing web apps using Puppeteer](docs/recipes/puppeteer.md) - [Testing web apps using Selenium WebDriverJS](docs/recipes/testing-with-selenium-webdriverjs.md) ## FAQ ### How is the name written and pronounced? AVA, not Ava or ava. Pronounced [`/ˈeɪvə/`](media/pronunciation.m4a?raw=true): Ay (f**a**ce, m**a**de) V (**v**ie, ha**v**e) A (comm**a**, **a**go) ### What is the header background? It's the [Andromeda galaxy](https://simple.wikipedia.org/wiki/Andromeda_galaxy). ### What is the difference between concurrency and parallelism? [Concurrency is not parallelism. It enables parallelism.](https://stackoverflow.com/q/1050222) ## Support - [GitHub Discussions](https://github.com/avajs/ava/discussions) ## Related - [eslint-plugin-ava](https://github.com/avajs/eslint-plugin-ava) — Lint rules for AVA tests - [@ava/typescript](https://github.com/avajs/typescript) — Test TypeScript projects - [@ava/cooperate](https://github.com/avajs/cooperate) — Low-level primitives to enable cooperation between test files - [@ava/get-port](https://github.com/avajs/get-port) — Reserve a port while testing ## Links - [AVA stickers, t-shirts, etc](https://www.redbubble.com/people/sindresorhus/works/30330590-ava-logo) - [Awesome list](https://github.com/avajs/awesome-ava) - [Do you like AVA? Donate here!](https://opencollective.com/ava) - [More…](https://github.com/avajs/awesome-ava) ## Team [](https://github.com/novemberborn) | [](https://github.com/sindresorhus) ---|--- [Mark Wubben](https://novemberborn.net) | [Sindre Sorhus](https://sindresorhus.com) ###### Former - [Kevin Mårtensson](https://github.com/kevva) - [James Talmage](https://github.com/jamestalmage) - [Juan Soto](https://github.com/sotojuan) - [Jeroen Engels](https://github.com/jfmengels) - [Vadim Demedes](https://github.com/vadimdemedes) ---