AVA Tutorial

AVA Tutorial

posted in javascript on

avajs/ava : 🚀 Testing can be a drag. AVA helps you get it done.


A concurrent test runner from the Andromeda galaxy. If I was to stray from Jasmine then it could only be for a project Sindre Sorhus is working on :) The screenshots on the project site promised extensive assertion failure output with clean stack traces and built-in Promise, async/await, Observable and React component support. So I tried it out and yup, AVA delivered.

Get Started

# Install
npm init ava

# Run tests
npx ava --watch --verbose

AVA runs automatically ignore:

  • Directories called fixtures, helpers or node_modules
  • Files starting with an underscore (_)

Checkout the recipes for examples when working with TypeScript, Flow, Babel, React, Vue.js, Debugging with Chrome DevTools and WebStorm and much more… There is also a Common Pitfalls document should you run into some trouble early on.

Example

import test from 'ava';

// Parameter needs to be called `t`
// for enhanced assertion messages.
test('Basic test example', t => {
    // Test will fail if not exactly
    // two assertions are made
    t.plan(2);
    t.pass();
    // Skipped tests still count to the plan
    t.true.skip();


    t.log(...values);


    // Test fails if exceeded.
    // Resets after each assertion.
    // There is no default timeout.
    // Globally from CLI: --timeout=10s
    t.timeout(ms);
});


// HOOKS:
// Runs before the first test in the file
// Runs before beforeEach hooks, if any.
test.before('optional title', t => {
    t.context.data = 'Shared with the tests';
    // t.context is an object by default but may be overwritten
});

// Runs after the last test in the file
test.after(() => {});

// Individual test setup/teardown
test.beforeEach(() => {});
test.afterEach(() => {});

// Will run even if other hooks or the test fails
test.afterEach.always(() => {});
// Also: test.after.always()

// Hooks run concurrently by default, unless:
test.serial.before(() => {});

Assertions

Assertions

You can use any assertion library instead of or in addition to the built-in one, provided it throws exceptions on failures.

All built-in assertions have a last optional message parameter.

import test from 'ava';

test('Built-in', t => {
    t.pass('optional message');
    t.fail();

    t.true(value); // t.false()
    t.truthy(value); // t.falsy()

    t.is(value, expected); // Object.is() comparison
    t.not(value, expected); // Inverted t.is()

    t.regex(contents, regex); // Also: notRegex()

    t.deepEqual(value, expected); // Also: notDeepEqual()
});

power-assert

power-assert-js/power-assert : Very fancy assertion failure reporting

// Outputs failures with power-assert when value is falsy
t.assert(value, [message]);

Macros

Create custom testers with macros.

function macro(t, input, expected) {
  t.is(input, expected);
}
test('static title', macro, 'input', 'expected');


// Dynamic Titles
macro.title = (providedTitle = undefined, input, expected) => '';

test(macro, 'input', 'expected');
test('providedTitle', macro, 'in', 'expected');

Exceptions

test('throws error', t => {
    const err = t.throws(fn, [expected]); // Also: .notThrows()
    // with expected either
    // - A constructor (instanceof)
    // - A string (error.message comparison)
    // - A RegExp (error.message match)
    // - A matcher object. Ex:
    const matcher = {
        instanceOf: TypeError,
        is: 'Object.is()',
        message: 'string | RegExp',
        name: 'err.name',
        code: 'err.code',
    };
    // When omitting expected, you still need to
    // pass null if you want to set the message.
});


test('throws async', async t => {
    await t.throwsAsync(async () => {
        throw new TypeError('oops');
    }, {instanceOf: TypeError, message: 'oops'});
    // Also: .notThrowsAsync()
    
    const error = await t.throwsAsync(() => Promise.reject('aargh'));
    t.is(error.message, 'aargh');
});

Not included

Workflow

test.only('Like Jasmine fit', t => {
    t.pass();
});

test.skip('Like Jasmine xit', t => {
    t.pass();
});

test.todo('Really need to write this test');

test.failing('Passes only when the test does NOT pass', t => {
    t.fail();
});

Command Line

CLI

Always runs the local AVA installation if available.
CLI takes precedence over configuration.

npx ava --help

# Default arguments
npx ava test.js test-*.js test/**/*.js **/__tests__/**/*.js **/*.test.js

npx ava
    --watch     # or -w 
    --verbose   # or -v: Outputs all test titles
    --match     # or -m: Run only matching test titles
    --timeout   # or -T: in ms. Or: "30s" (30 seconds), "3m" (3 minutes)
    --fail-fast # Stop on first failure

# Case insensitive test title matching
# Overrides .only
npx ava -m='startWith*' -m='!*notContains*' -m='exact'

sindresorhus/matcher : Matcher used for --match

Other CLI parameters

  • --serial, -s: Run tests serially
  • --concurrency, -c: Max tests running at the same time (Default: # CPU Cores)
  • --tap, -t: Generate Test Anything Protocol output
  • --color and --no-color: With color by default
  • --reset-cache

Configuration

Configuration

In your package.json:

{
    "ava": {
        "files": [
            "my-test-directory/**/*.js",
            "!my-test-directory/exclude-this-directory/**/*.js",
            "!**/exclude-this-file.js"
        ],
        "sources": [ // changes to matching files re-run tests while in --watch mode
            "**/*.{js,jsx}",
            "!dist/**/*"
        ],
        "failWithoutAssertions": false, // True by default: a test without assertion fails
        "compileEnhancements": false,   // Disable power-assert
        "require": [
            "@babel/register"
        ],
        "babel": {
            "extensions": ["jsx"],
            "testOptions": {
                "babelrc": false
            }
        },
        "a-cli-arg": "ex: 'failFast': true"
    }
}
  • Can also add json nodes matching the CLI parameters for failFast, tap, verbose, timeout and match: string[]
  • cache: true: Use cache in node_modules/.cache/ava. false: Use temp dir.
  • Also possible to create a ava.config.js in the same folder as your package.json.

Async Support

test('Promise support', t => {
    return Promise.resolve(true).then(result => {
        t.true(result);
    });
});

test('async/await support', async t => {
    const value = await Promise.resolve(true);
    t.true(value);
});

test('Observable support', t => {
    return of(1).pipe(map(() => t.pass()));
});

test.cb('Callback support with t.end', t => {
    // Any truthy value passed as first argument
    // to `t.end` will fail the test
    fs.readFile('data.txt', t.end);
});


test.afterEach.cb('This stuff also works for hooks', t => {
    setImmediate(t.end);
});


console.log('Currently running', test.meta.file);

Other interesting reads
Tags: testing tutorial