AVA Testing Framework Cheat Sheet

Introduction

AVA is a minimalistic JavaScript test runner designed for Node.js, focusing on speed, simplicity, and concurrent test execution. It provides a clean, distraction-free testing environment with isolated test files, modern JavaScript support, and no implicit globals.

Why AVA Matters

  • Concurrency: Runs tests in parallel for faster execution
  • Simplicity: Minimal API with focused functionality
  • Isolation: Each test file runs in its own Node.js process
  • Modern JS: Full ES6/ES2015+ support via Babel
  • Clean Output: Simple, readable test output format
  • Snapshot Testing: Built-in snapshot capability

Installation & Setup

Basic Installation

# Install as a dev dependency
npm install --save-dev ava

# Or using Yarn
yarn add --dev ava

Configuration in package.json

{
  "scripts": {
    "test": "ava"
  },
  "ava": {
    "files": [
      "test/**/*.test.js",
      "!**/node_modules/**"
    ],
    "concurrency": 5,
    "failFast": true,
    "failWithoutAssertions": false,
    "verbose": true,
    "timeout": "30s"
  }
}

Configuration with ava.config.js

export default {
  files: ['test/**/*.test.js'],
  concurrency: 5,
  failFast: true,
  verbose: true
};

Test File Structure

Basic Test Structure

import test from 'ava';

test('my test name', t => {
  t.pass();
});

Setup and Teardown

import test from 'ava';

test.before(t => {
  // Runs before all tests
});

test.after(t => {
  // Runs after all tests
});

test.beforeEach(t => {
  // Runs before each test
});

test.afterEach(t => {
  // Runs after each test
});

Core Assertions

AssertionDescriptionExample
t.pass()Test passest.pass()
t.fail()Test failst.fail()
t.truthy()Value is truthyt.truthy(value)
t.falsy()Value is falsyt.falsy(value)
t.true()Value is truet.true(value)
t.false()Value is falset.false(value)
t.is()Strict equalityt.is(value, expected)
t.not()Strict inequalityt.not(value, expected)
t.deepEqual()Deep equalityt.deepEqual(value, expected)
t.notDeepEqual()Not deep equalt.notDeepEqual(value, expected)
t.like()Subset equalityt.like(value, subset)
t.throws()Function throwst.throws(fn, [expected])
t.notThrows()Function doesn’t throwt.notThrows(fn)
t.regex()String matches regext.regex(string, regex)
t.notRegex()String doesn’t match regext.notRegex(string, regex)
t.snapshot()Match to snapshott.snapshot(value)

Advanced Test Techniques

Async Testing

// Promises
test('async promise test', t => {
  return somePromise().then(result => {
    t.is(result, 'expected');
  });
});

// Async/await
test('async/await test', async t => {
  const result = await somePromise();
  t.is(result, 'expected');
});

Test Modifiers

// Only run this test
test.only('will run', t => {
  t.pass();
});

// Skip this test
test.skip('will not run', t => {
  t.fail();
});

// Only run in specific environment
test.serial('runs serially', t => {
  t.pass();
});

// Test runs but expected to fail
test.failing('expected to fail', t => {
  t.fail();
});

// Todo test (reminder to implement)
test.todo('implement this test later');

Snapshot Testing

test('snapshot test', t => {
  t.snapshot({
    foo: 'bar',
    baz: 'qux'
  });
});

Macros (Reusable Tests)

import test from 'ava';

// Define a test macro
const macro = (t, input, expected) => {
  t.is(input, expected);
};

// Use the macro with different inputs
test('macros-1', macro, 'foo', 'foo');
test('macros-2', macro, 'bar', 'bar');

// Macro with title function
macro.title = (providedTitle, input, expected) => 
  `${providedTitle}: ${input} is ${expected}`;

Common Testing Patterns

Testing React Components with AVA

// Using with React Testing Library
import test from 'ava';
import { render, fireEvent } from '@testing-library/react';
import React from 'react';
import Button from '../components/Button';

test('Button renders correctly', t => {
  const { getByText } = render(<Button>Click me</Button>);
  t.truthy(getByText('Click me'));
});

test('Button fires onClick event', t => {
  let clicked = false;
  const { getByText } = render(
    <Button onClick={() => { clicked = true; }}>Click me</Button>
  );
  
  fireEvent.click(getByText('Click me'));
  t.true(clicked);
});

Testing APIs and HTTP Requests

// Using with Supertest
import test from 'ava';
import request from 'supertest';
import app from '../app';

test('GET /users returns 200', async t => {
  const res = await request(app).get('/users');
  t.is(res.status, 200);
});

// Using with Nock for mocking HTTP requests
import test from 'ava';
import nock from 'nock';
import { fetchUser } from '../api';

test('fetchUser makes correct API call', async t => {
  nock('https://api.example.com')
    .get('/users/1')
    .reply(200, { id: 1, name: 'John' });
  
  const user = await fetchUser(1);
  t.is(user.name, 'John');
});

Testing with Mocks and Stubs

import test from 'ava';
import sinon from 'sinon';
import * as database from '../database';
import { saveUser } from '../users';

test('saveUser calls database correctly', async t => {
  // Create a stub for database.save
  const saveStub = sinon.stub(database, 'save').resolves({ success: true });
  
  await saveUser({ id: 1, name: 'John' });
  
  // Verify the stub was called with correct arguments
  t.true(saveStub.calledWith({ id: 1, name: 'John' }));
  
  // Restore the original function
  saveStub.restore();
});

Common Challenges and Solutions

ChallengeSolution
Tests too slowIncrease concurrency in AVA config; minimize setup/teardown operations
Test environment setupUse test.before() hooks to initialize environment once
Flaky testsEnsure proper isolation; avoid dependencies between tests; use timeouts
Database testingUse test databases or in-memory databases; properly clean up after tests
Mocking dependenciesUse sinon, proxyquire, or mock-require to replace dependencies
Isolating testsLeverage AVA’s process isolation; avoid shared state between tests
Coverage reportingIntegrate with NYC/Istanbul for coverage metrics

Best Practices

Naming Conventions

  • Use descriptive test names that explain the expected behavior
  • Follow a consistent pattern like “should [expected behavior] when [condition]”
  • Group related tests in separate files following the structure of your source code

Organizing Tests

  • Keep test files close to the code being tested
  • Split large test suites into multiple files
  • One test file per module/component being tested

Test Structure

  • Keep tests simple and focused on a single aspect
  • Follow the Arrange-Act-Assert pattern
  • Minimize dependencies between tests

Performance Tips

  • Use t.plan() to ensure all assertions run
  • Utilize AVA’s concurrency features
  • Keep setup/teardown code minimal and fast

Debugging

  • Use t.log() to output debug information
  • Run specific tests with npx ava test-file.js
  • Use --verbose flag for detailed output

Command-Line Options

FlagDescription
--watch, -wRe-run tests when files change
--match, -mOnly run tests matching pattern
--update-snapshots, -uUpdate snapshots
--fail-fastStop after first test failure
--timeout, -TSet global timeout
--verbose, -vEnable verbose output
--serial, -sRun tests serially
--concurrency, -cMax number of test files running at the same time

AVA vs Other Testing Frameworks

FeatureAVAJestMochaTape
Parallel Testing✅ Built-in✅ Built-in❌ Requires plugins❌ No
Assertions✅ Built-in✅ Built-in❌ Requires assertion lib✅ Built-in
Snapshot Testing✅ Built-in✅ Built-in❌ Requires plugins❌ No
Test Isolation✅ Process-level✅ Module-level❌ Limited❌ Limited
Configuration✅ Minimal✅ Extensive✅ Moderate✅ Minimal
Learning CurveModerateModerateLowLow
Community/PluginsModerateExtensiveExtensiveMinimal
Built-in Mocking❌ No✅ Yes❌ No❌ No

Resources for Further Learning

Official Resources

Tutorials and Guides

Complementary Tools

Scroll to Top