以前常用的test runner还是mocha。mocha的确是一个不错的测试框架,但是会预设一些全局变量如describe、it等。而且测试用例是串行跑的,有时跑完整个测试需要很久。
ava声称是未来的test runner。它确实有很多优点,并发运行测试,确保每个测试用例原子性的同时,能大大提高测试的效率。

安装

全局安装

1
npm install ava -g

然后可以使用全局的ava命令来初始化项目:

1
ava --init

这样就会把ava加到scripts的test下,并把ava加到devDependencies中。

1
2
3
4
5
6
7
8
9
{
"name": "awesome-package",
"scripts": {
"test": "ava"
},
"devDependencies": {
"ava": "^0.11.0"
}
}

局部安装
本地安装ava

1
npm install ava --save-dev

然后配置scripts的test命令,值为ava。

个人习惯使用ava -v 这样就可以verbose输出。

命令行

常用参数

1
2
--serial -s 是否并行
--verbose -v 是否verbose输出(会输出每个测试的title)

运行方式

1
2
ava
ava demo.js

文件夹会被递归遍历,带上 *.js 参数的话全部文件都会被作为测试文件。名字为 fixtures,helpers 和 nodemodules 的文件夹总会被忽略。所以把 helper 名字以 开头命名就可以一起放置在测试文件的目录下。

当使用 npm test 时你可以直接传参数 npm test test2.js,但标志需要像这样传递 npm test – –verbose。

配置

所有的选项都可以配置在package.json的ava属性中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"ava": {
"files": [
"my-test-folder/*.js",
"!**/not-this-file.js"
],
"source": [
"**/*.{js,jsx}",
"!dist/**/*"
],
"match": [
"*oo",
"!foo"
],
"failFast": true,
"tap": true,
"require": [
"babel-register"
],
"babel": "inherit"
}
}

测试文件书写注意事项

测试用例结构

可以通过导入ava的test方法来创建一个测试。提供可选的title和callback函数。该函数会在运行测试时被调用。函数只有一个参数t(常用名t,当然也可以用别的名),t为一个执行对象。

1
2
3
4
5
const test = require('ava');
test(<标题>,t => {
t.is(1, 2);
});

每个测试用例可以使用cb方式以及promise方式。

promise

如果测试返回的是一个promise,则不需要使用t.end来结束一个测试。

1
2
3
4
5
test(t => {
return somePromise().then(result => {
t.is(result, 'unicorn');
});
});

callback

callback模式使用test.cb来创建一个测试,并且需要使用t.end来结束测试.

1
2
3
4
test.cb(t => {
// t.end 自动检查第一个参数是否为错误
fs.readFile('data.txt', t.end);
});

钩子函数

ava提供四种钩子函数before,after,beforeEach和afterEach。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
test.before(t => {
// 这个会在所有测试前运行
});
test.before(t => {
// 这个会在上面的方法后面运行,但在测试之前运行
});
test.after('cleanup', t => {
// 这个会在所有测试之后运行
});
test.beforeEach(t => {
// 这个会在每个测试之前运行
});
test.afterEach(t => {
// 这个会在每个测试之后运行
});
test(t => {
// 正常的测试
});

AVA 运行每个测试文件会有各自单独的进程,你可能不需要在 after 钩子中清理全局状态,因为它只会在进程退出前被调用。

beforeEach 和 afterEach钩子可以共享测试的上下文:

1
2
3
4
5
6
7
test.beforeEach(t => {
t.context.data = generateUniqueData();
});
test(t => {
t.is(t.context.data + 'bar', 'foobar');
});

默认情况下 t.context是一个对象,但你可以重新赋值:

1
2
3
4
5
6
7
test.beforeEach(t => {
t.context = 'unicorn';
});
test(t => {
t.is(t.context, 'unicorn');
});

上下文共享在 before 和 after 钩子中不可用。

only和skip

在运行的测试项加only,将会只运行这个测试。
在运行的测试项加skip,将会只跳过这个测试。

1
2
test.only(title, t => {});
test.skip(title, t => {});

常用断言

1
2
3
4
5
6
7
- t.is(value, expected) 等于
- t.true(value) true
- t.false(value) false
- t.not(value, expected)不等于
- t.deepEqual(value, expected)全等于
- t.regex(content, regex)匹配正则表达式
- t.notRegex(content, regex)不匹配正则