訳注
これはreadme.mdの日本語訳です。こちらがAVAのmasterブランチとの差分のリンクになります(このリンクをクリックして、readme.md
に変更点が見当たらなければ、この翻訳が最新であることを意味します)。
未来型のテストランナー
JavaScript自体はシングルスレッドですが、Node.jsにおけるIOは、その非同期の性質によって並列で実行可能です。AVAは、この事が利点であり、テストを同時に実行することができ、特にIOが重いテストで効果があります。また、テストファイルは独立したプロセスで並列して実行することで、より良いパフォーマンスと各テストファイル毎に分離された環境にすることができます。PageresにおけるMochaからAVAへの切り替えによって、テストにかかる時間を31秒から11秒まで減らしました。並列実行するテストにすると、大域の状態や他のテストの状態から影響を受けないことを意味するようになる粒度が極小のテストを書くようになります。これはすごい事ですよね!
*Issueやプルリクエストなどで貢献したい場合は、コントリビューションガイドを読んでください。
アップデートの情報のためにAVAのTwitterアカウントをフォローしてください。
翻訳: Español, Français, Italiano, 日本語, Português
- 軽量で高速
- テストのシンプルな文法
- テストを並列で実行
- 粒度が極小のテストを書かせる
- 暗黙のグローバルを排除
- テストファイル毎に分離された環境
- ES2015でテストコードを記述可能
- Promiseのサポート
- Generatorのサポート
- Asyncのサポート
- Observableのサポート
- 強化されたassert
- オプション付きのTAP出力
- 明快なスタックトレース
import test from 'ava';
test(t => {
t.same([1, 2], [1, 2]);
});
AVAを$ npm install --global ava
でグローバルにインストールして、$ ava --init
(他にどんなオプションでも付けられます)を実行してpackage.jsonにAVAを追加するか手動で作成します。
{
"name": "awesome-package",
"scripts": {
"test": "ava"
},
"devDependencies": {
"ava": "^0.11.0"
}
}
test.js
という名前のファイルをプロジェクトのルートディレクトリに作ってください。
import test from 'ava';
test('foo', t => {
t.pass();
});
test('bar', async t => {
const bar = Promise.resolve('bar');
t.is(await bar, 'bar');
});
$ npm test
$ ava --help
Usage
ava [<file|folder|glob> ...]
Options
--init Add AVA to your project
--fail-fast Stop after first test failure
--serial, -s Run tests serially
--require, -r Module to preload (Can be repeated)
--tap, -t Generate TAP output
--verbose, -v Enable verbose output
--no-cache Disable the transpiler cache
Examples
ava
ava test.js test2.js
ava test-*.js
ava test
ava --init
ava --init foo.js
Default patterns when no arguments:
test.js test-*.js test/**/*.js
CLIはグローバルで実行できる場合でも、実行可能であればローカルにインストールされたAVAを実行します。
ディレクトリはデフォルトで再帰的です。fixtures
やhelpers
と名付けられたディレクトリは無視され、_
で始まるファイルも同様です。これはテストファイルと同じディレクトリにヘルパーを置くのに役立ちます。
npm test
を実行するときに、引数を使ってnpm test test2.js
のようにテストの位置を直接渡すことができます。しかし、フラグはnpm test -- --verbose
のように渡す必要があります。
CLIの全てのオプションは、package.json
のava
のセクションで設定できます。この設定でava
コマンドのデフォルトの動作を変更することができるので、同じオプションをコマンドプロンプトで繰り返してタイプする必要がなくなります。
{
"ava": {
"files": [
"my-test-folder/*.js",
"!**/not-this-file.js"
],
"failFast": true,
"serial": true,
"tap": true,
"verbose": true,
"require": [
"babel-core/register"
]
}
}
CLIに渡される引数は常にpackage.json
の設定よりも優先されます。
テストは非同期で実行され、サポートされたasyncオブジェクト(promiseかobservable)で返す必要があります。私たちは、async関数を使うことを強く推奨します; これはasyncのコードを簡潔で読みやすいものにして、暗黙のうちにpromiseを返すので、自分では明示的に返す必要はありません。
上記のサポート対象のasyncオブジェクトの中の1つも返さなければ、同期で即座に終了すると考えられます。
promiseかその他のサポート対象のasyncオブジェクトを使用できなければ、test.cb([title], fn)
でテストを記述することで"コールバックモード"を有効にできます。この方法で宣言されるテストは、t.end()
によって手動で終了しなければなりません。このモードは主にコールバックスタイルのAPIをテストするためのものです。
全てのテストは同期で定義しなければなりません。テストはsetTimeout
やsetImmediate
などの中に記述することはできません。
テストのファイルはそれらのカレントディレクトリで実行されるので、process.cwd()
は常に__dirname
と同じになります。path.join(__dirname, 'relative/path')
とする代わりに相対パスを使用することができます。
テストをつくるには、AVAからrequire
したtest
関数を呼び出して、任意のテスト名とテストの実行する関数を与えます。渡された関数は第1引数としてコンテキストを与えられますが、ここではAVAのメソッドとアサーションを呼び出すことが出来ます。
test('name', t => {
t.pass();
});
テストに名前をつけるのは任意ですが、1つ以上のテストがある場合には名前を使用することを推奨します。
test(t => {
t.pass();
});
また名前付き関数をテスト名の代わりに選ぶことも出来ます:
test(function name(t) {
t.pass();
});
アサーションプランは特定数のアサーションを確保するのに使用されます。一番よくある状況としては、これでアサーションの期待された数の実行前に終了しなかったテストを確認するような状況です。コールバックやループの中でアサーションがある時に有用なのが、あまりに多くのアサーションが実行されたらテストが失敗することです。注意してほしいのは、AVAはnode-tapやtapeとは違い、計画されたアサーション数に到達しても自動終了はしないという点です。
これは成功するテストになります:
test(t => {
t.plan(1);
return Promise.resolve(3).then(n => {
t.is(n, 3);
});
});
test.cb(t => {
t.plan(1);
someAsyncFunction(() => {
t.pass();
t.end();
});
});
並列実行は素晴らしいものですが、一方で並列で処理できないものもあります。このようなまれなケースでは、並列のテストの前に直列でテストを強制的に実行する、test.serial
を呼び出すことができます。
test.serial(t => {
t.pass();
});
Only-testsはテストを限定的に実行することを強制します。開発時に幾つかのテストだけを実行したいときに役立ちます。
test('will not be run', t => {
t.fail();
})
test.only('will be run', t => {
t.pass();
});
Skip-testsはスキップされたことを出力はしますが、絶対に実行しません。
test.skip('will not be run', t => {
t.fail();
});
setupとteardownの両方または、いずれかが必要なとき、test()
と同じように、test.before()
とtest.after()
を利用できます。test.before()
とtest.after()
に与えられたテスト関数は、全てのテストの前/後で呼び出されます。また各テストにsetup/teardownが必要なときは、test.beforeEach()
とtest.afterEach()
が利用できます。必要なだけ追加してください。失敗したときに表示されるタイトルを指定することも必要に応じて出来ます。
test.before(t => {
// 全てのテストの前に実行します
});
test.before(t => {
// テストの前に実行しますが、上記の処理の後です
});
test.after('cleanup', t => {
// 全てのテストの後に実行します
});
test.beforeEach(t => {
// それぞれのテストの前に実行します
});
test.afterEach(t => {
// それぞれのテストの後に実行します
});
test(t => {
// 通常のテスト
});
どのフックでも、asyncを使うこと、asyncオブジェクトを返すこと、"コールバックモード"を有効にすることも、出来ます。
test.before(async t => {
await promiseFn();
});
test.cb.beforeEach(t => {
setTimeout(t.end);
});
test.afterEach.cb(t => {
setTimeout(t.end);
});
test.after(t => {
return new Promise(/* ... */);
});
beforeEach
とafterEach
のフックは特定のテストでコンテキストを共有できます:
test.beforeEach(t => {
t.context.data = generateUniqueData();
});
test(t => {
t.is(t.context.data + 'bar', 'foobar');
});
このコンテキストは、デフォルトでは1つのオブジェクトですが、直接アサインすることも出来ます。
test.beforeEach(t => {
t.context = 'unicorn';
});
test(t => {
t.is(t.context, 'unicorn');
});
テストの修飾子を以下のようにチェーンすることができます。
test.before.skip([title], testFn);
test.skip.after(....);
test.serial.only(...);
test.only.serial(...);
これはテストで、他の修飾子の振る舞いや情報を失わないように一時的にskip
やonly
を使うときに、特に役立ちます。
AVAのデフォルトに加えて、または代わりとしてどのようなアサーションモジュールを利用することも可能ですが、今はまだ.plan()
メソッドを使うことはできません。
import assert from 'assert';
test(t => {
assert(true);
});
AVAは、Babel 6を使ってES2015をサポートしています。単にES2015でテストを書いてください。他の環境整備は必要ありません。プロジェクト内でバージョンを問わずにBabelを利用することができます。AVAは、es2015
とstage-2
のプリセットをバンドルしたBabelを使用しています。
現時点でのAVAは、実行するようにしたテストだけをトランスパイルします。*テストの外からimport
したモジュールはトランスパイルしません。*このアプローチを採用したのには妥当な理由がありますが、期待した挙動ではないかもしれません!
シンプルな代替策として、Babel's require hookを使ってインポートされるモジュールをリアルタイムにトランスパイルすることができます。AVAはES2015モジュールをサポートしてるのでrequireフックとして使用ができるからです:
import test from 'ava';
import 'babel-core/register';
import foo from './foo'; // <-- ES2015 で書くことが出来ます!
test('foo bar', t => {
t.same('baz', foo('bar'));
});
#111が、将来性のある拡張として、議論されています。
テスト中でpromiseを返すと、promiseがresolveしたときに終了するので、明示的にテストを終了する必要はありません。
test(t => {
return somePromise().then(result => {
t.is(result, 'unicorn');
});
});
AVAはgenerator関数をビルトインサポートしています。
test(function * (t) {
const value = yield generatorFn();
t.true(value);
});
AVAはasync *(async/wait)*をビルトインサポートしています。
test(async function (t) {
const value = await promiseFn();
t.true(value);
});
// asyncのアロー関数
test(async t => {
const value = await promiseFn();
t.true(value);
});
AVAはObservableをビルトインサポートしています。 テストからobservableを返すと、AVAはテストが終了する前にobservableを自動的に完了します。
"コールバックモード"やt.end()
は必要ありません
test(t => {
t.plan(3);
return Observable.of(1, 2, 3, 4, 5, 6)
.filter(n => {
// 偶数のみ
return n % 2 === 0;
})
.map(() => t.pass());
});
AVAはnodeスタイルのエラーファーストのコールバックAPIを使用したときに、最終コールバックとしてt.end
の使用をサポートしています。AVAはt.end
に渡される第1引数がどんな真と判断される値もエラーと見なします。t.end
は、test.cb
チェーンを利用することで有効になる、"コールバックモード"が必要になるということを覚えておいてください。
test.cb(t => {
// t.endは自動的に第1引数としてエラーをチェックします。
fs.readFile('data.txt', t.end);
});
AVAは--tap
オプションの指定でどのようなTAPレポーターを使用しても、TAPの出力をすることができます。
$ ava --tap | tap-nyan
エラーの箇所をより素早く見つけられるように、AVAはスタックトレースから自動的に無関係の行を取り除きます。
Type: string
テストのタイトルです。
Type: function
実際にテストが含まれている必要があります。
テスト関数に渡され、個別のAVAのメソッドとアサーションが含まれます。
テストの中でいくつのアサーションがあるかを計画します。計画されたアサーションと実際のアサーションの数が一致しない場合、テストは失敗します。
テストを終了します。test.cb()
と一緒に使用しないと動きません。
アサーションはテストのコンテキストに合成されています:
test(t => {
t.ok('unicorn'); // アサーション
});
1つのテストの中で複数のアサーションの失敗が発生したとき、AVAは最初の1つだけを表示します。
アサーションを通します。
アサーションを失敗させます。
value
が真と判断できる値とします。
value
が偽と判断できる値とします。
value
がtrue
であるとします。
value
がfalse
であるとします。
value
がexpected
と同じ値であるとします。
value
がexpected
と同じ値ではないとします。
value
がexpected
と厳密に同じ値であるとします。
value
がexpected
と厳密に同じ値ではないとします。
エラーを投げるfunction
かpromise
のrejectであるとします。
error
は、コンストラクタ、正規表現、エラーメッセージ、バリデーション関数などでも大丈夫です。
error
を投げないfunction
かpromise
のresolveであるとします。
contents
がregex
にマッチするとします。
error
が偽と判断できる値とします。
skip
修飾子を使うことでどのアサーションもスキップできます。スキップされたアサーションはカウントされるので、計画したアサーションの数を変更する必要はありません。
test(t => {
t.plan(2);
t.skip.is(foo(), 5); // スキップするときにplanの数を変更する必要はありません。
t.is(1, 1);
});
AVAには、より説明的なアサーションメッセージを与える、power-assert
がビルトインとして付属してます。これにより、テストを読んでよりコード内部の情報を得ることができます。
以下のようなテストの場合:
test(t => {
const x = 'foo';
t.ok(x === 'bar');
});
この場合、普通はほとんど手助けにならない出力になります:
false === true
強化されたassertでは以下のような結果が得られます:
t.ok(x === 'bar')
|
"foo"
実際はこのような場合にはt.is()
を使えますし、そうしたほうが良いですが、これはただの簡単な例です。
もっと高度な例を試しましょう:
test(t => {
const a = /foo/;
const b = 'bar';
const c = 'baz';
t.ok(a.test(b) || b === c);
});
この結果は以下です:
t.ok(a.test(b) || b === c)
| | | |
| "bar" "bar" "baz"
false
全てのassertのメソッドは強化されています。
楽しんでください!
それぞれのテストファイルは分離されたNode.jsのプロセスで実行されます。これには多くの利点があります。それぞれのテストファイルは互いに影響することはありません。グローバル環境でのテストファイルのモック使用、ビルトインの機能を上書きしたりなどなどです。しかし、主にパフォーマンスの理由によりこうしています。Node.jsは非同期IOを同時に実行できますが、テストがメインスレッドをブロックするような、重い同期処理の場合はあまり役に立ちません。テストを同時に実行して並列でテストすることで、モダンシステムの利点を最大限に活用できます。
テストを並列で行うことには色々な問題が伴いますが、IOがその1つです。一般的に、直列のテストではカレントディレクトリに一時的なディレクトリを作成して、終了時にそれを削除します。並列ではテストがお互いに競合するのでこれはできません。正しいやり方としては、それぞれのテストが新規に一時ディレクトリを利用することです。tempfile
やtemp-write
といったモジュールが役立つでしょう。
AVAはデフォルトで並列にテストを実行しますが、何かデバッグする必要があるときには最善ではありません。代わりに--serial
オプションをつけてテストを直列で実行してください:
$ ava --serial
AVAが実行するテストファイルのコードカバレッジにistanbul
を使うことは出来ませんが、代わりにサブプロセスのサポートがあるistanbul
であるnyc
を利用できます。
バージョン5.0.0
では、トランスパイルに関係なく、実際のコードのカバレッジをレポートするのにソースマップを使います。テスト対象のコードがインラインのソースマップかソースマップファイルへの参照のいずれかを含んでいることを確認してください。babel/register
を使っていれば、.babelrc
でsourceMaps
オプションをinline
に設定できます。
Mochaはデフォルトのインターフェース(多くの人々が利用している)でdescribe
やit
のような暗黙のグローバルを利用することを必須になっており、強い主張もなく、肥大化していて、デフォルトで同期で、プログラム的なAPIもなくて、直列でテストを実行して、そして遅いです。Tapeやnode-tapはかなり良いです。AVAはそれらのシンタックスに強く影響を受けています。ですが、これらはいずれも、テストを直列で実行し、TAPを第1級オブジェクトとして作り上げ、私の見方ですが、これがコードベースを複雑で結合したものにしました。TAPの出力は読みづらいので、外部のレポーターを利用することになります。AVAは自己主張が強く並列です。デフォルトの簡単なレポーターがあり、CLIのフラグを通じてTAPもサポートしています。
TAP reporterの中の何でも一つを--tap
フラグから使ってください。
AVAで、Avaやavaではありません。発音は/ˈeɪvə/
ay-vəです。
Sindre Sorhus | Vadim Demedes | James Talmage |