Mark Wubben. I’m a humanist & technologist who loves working with the web. I’m here today to talk about AVA, the futuristic test runner. I’m currently based here in London, mixing open source development with contracting.
AVA tries to make it less so. I’d even say makes testing fun, but that’s rarely the case. Also, it’s Mocha’s slogan Let’s quickly go through some of the ways in which AVA helps make testing less frustrating.
tests, even if you’re not using it in the rest of your codebase. Which is fantastic because it lets you write tests without returning promises all the time. This makes testing asynchronous code a lot easier.
library. AVA comes with “power-assert” built-in. This library, by Takuto Wada, lets you write assertions using plain JavaScript expressions. If the assertion fails it’ll show you what the values were, so you can easily spot the error. power-assert: https://github.com/power-assert-js/
| | 2 | -1 0 false [1,2,3] Here’s an example of when an assertion failed. You can see the assertion in question and each intermediate value. You can easily see that the array does not include zero without having to write debug statements. It’s also easier to see what it is your testing without having to look up the documentation of a particular assertion method. Now you don’t need to memorise Chai!
test interfaces you may be used to, like BDD, TDD or an exports object. Instead you get a test function which you can call with a title and test implementation.
=> { t.true(1 + 1 === 2) }) AVA lets you write tests, but not test hierarchies. This lets you focus on testing what your program does, without having to mimic how it’s structured. If you have more than one test file AVA will automatically prepend the file name to the test titles, so there’s no need to even repeat it in the file itself.
test file. This means you can safely modify global state without accidentally affecting unrelated tests. It can also run multiple test files concurrently, each in their own process.
November 2014. Other members of the core team are Vadim Demedes, James Talmage and Juan Soto. We’re there to help with issues and contributions from others. Whilst we’re quite familiar with AVA’s internals none of us understand all of it. Major contributions have been made by people not on the core team — it’s not about us, it’s about building a great test runner.
the documentation and recipes on how to use AVA are incredibly valuable. (Those “closed” PRs were merged manually, before GitHub released squash&merge.)
process. But within each test file it by default starts all tests at the same time. So if you have asynchronous tests these will run concurrently, hopefully resulting in a faster test execution.
=> setTimeout(resolve, 500)) console.log('finish first') }) test('second', async t => { console.log('start second') await new Promise(resolve => setTimeout(resolve, 100)) console.log('finish second') }) This example has two asynchronous tests. The second will finish before the first.
=> setTimeout(resolve, 500)) console.log('finish first') }) test('second', async t => { console.log('start second') await new Promise(resolve => setTimeout(resolve, 100)) console.log('finish second') }) console.log('start first') await new Promise(resolve => setTimeout(resolve, 500)) console.log('finish first') }) test('second', async t => { console.log('start second') await new Promise(resolve => setTimeout(resolve, 100)) console.log('finish second') }) You can mark individual test as serial. AVA will execute them first, in order, one at a time, before starting the remaining tests. (There’s also a --serial command line flag which causes all tests to run serially.)
hooks that run before the first test, and after the last. Note that due to process isolation you can usually get away with performing shared setup in the test file itself, and you don’t necessarily have to clean up either. (Any code that runs when the test file is loaded delays when tests actually start running, so there is an impact, but I find it nicer not to use test.before)
t.true(t.context.foo === 'bar') }) beforeEach and afterEach hooks receive the test object. This is mostly useful because of the context property that is shared with the tests. You can set up a new context for each test. (t.context can also be assigned to.)
test to focus on while developing', t => {}) The .skip and .only modifiers should only be used during development. They can be useful when debugging a test, e.g. you can skip a test that distracts you, or run just those tests you’re having trouble getting right.
showcases a bug', t => {}) The .todo modifier can be used when sketching out tests. They’re included in the AVA’s output so you won’t forget about them, which is better than using code comments. The .failing modifier is useful if you or a contributor knows how to reproduce a bug, but not yet how to solve it. You can check in the failing test without it breaking CI. This way a contributor gets the credit for finding the bug, and somebody else already has a test case for when they go try and fix it.
{ t.true(err === null) t.true(result === 42) t.end() }) }) The .cb modifier can be used when testing (asynchronous) functions that invoke a callback. You must end these tests using t.end().
{ t.true(err === null) t.true(result === 42) t.end() }) }) test.cb('doSomething() invokes the callback', t => { doSomething((err, result) => { t.true(err === null) t.true(result === 42) t.end() }) }) The .cb modifier can be used when testing (asychronous) functions that invoke a callback. You must end these tests using t.end().
=> { const result = await pify(doSomething)() t.true(result === 42) }) That said, you’re probably better off using async/await and promisifying the callback-taking function. AVA can’t trace uncaught exceptions back to the test. Using a promise you’re less likely to run into this problem. Here I’m using pify to promisify the function. See https://www.npmjs.com/package/pify.
+ 2 === 4', macro, '2 + 2', 4); test('2 * 3 === 6', macro, '2 * 3', 6); Here the same test implementation is used for two different tests. Additional arguments are passed to the macro itself.
won’t use all of them. Let’s look at the most useful ones. You can find the full list in our documentation of course: https://github.com/avajs/ava#assertions.
function rather than passing it to t.throws(), causing the exception to be thrown and your test to fail), AVA does a pretty good job at catching it and warning you about it.
need to pass for your test to succeed. This can be useful in certain asynchronous or loop situations. If too few or too many assertions pass your test will fail. Note that AVA doesn’t automatically end your test once the expected count is reached.
change a test file it’ll rerun just that file. If you change a source file it’ll rerun all tests that depend on it. And if can’t figure out which test to rerun it just reruns all. Demo time!
this causes all tests in all test files to run serially. Note that AVA still runs test files concurrently. (You could also use these modifiers in your test script definition, of course, but you’re probably better off configuring them).
Use match to run just those tests that match the expression. The expression syntax is purposefully limited, you can find more details in the documentation: https:// github.com/avajs/ava#running-tests-with-matching-titles.
files or glob patterns as the last arguments. Make sure to use quotes around the glob patterns to prevent your shell from trying to expand them. The same goes for the match argument!
[ "*end", "start*" ], "serial": true, "timeout": "5s" "verbose": true } Use the AVA stanza to add your configuration. Some options can only be configured here.
files, all at once. If you have lots of test files that can be a bit extreme, to the point where your machine freezes up and your CI build fails. We’re working on several improvements in this area. For now you should use the concurrency flag and option.
limits AVA to five concurrent child processes. It’s currently an experimental feature while we figure out a good default and resolve issues with the .only modifier.
that you need to make sure babel-register is installed. babel-register can be quite slow. It’ll be more efficient if you precompile your source files and test your precompiled code. This is my preferred strategy. Also, you can configure the Babel config AVA uses to compile the test files. I won’t go into those details though, please see the documentation: https://github.com/avajs/ ava/blob/master/docs/recipes/babelrc.md.
want to make this work though, it’s just very difficult. That said, you can usually get pretty far by emulating the DOM. We’ve got a recipe for that: https://github.com/avajs/ava/blob/master/docs/recipes/browser-testing.md.
of the test file. This is more confusing than beneficial so we’ll be changing it. In the future the current working directory will be that of the package.json file.