メインコンテンツへスキップ
バージョン: 30.0

非同期コードのテスト

非公式ベータ版翻訳

このページは PageTurner AI で翻訳されました(ベータ版)。プロジェクト公式の承認はありません。 エラーを見つけましたか? 問題を報告 →

JavaScriptではコードが非同期で実行されることが一般的です。非同期コードをテストする場合、Jestはテスト対象のコードがいつ完了したかを認識してから次のテストに進む必要があります。Jestにはこれを処理するいくつかの方法があります。

Promise

テストからPromiseを返すと、JestはそのPromiseが解決されるまで待機します。Promiseが拒否(reject)された場合、テストは失敗します。

例えば、fetchDataが文字列'peanut butter'で解決(resolve)されるPromiseを返すとします。次のようにテストできます:

test('the data is peanut butter', () => {
return fetchData().then(data => {
expect(data).toBe('peanut butter');
});
});

Async/Await

代わりにテストでasyncawaitを使用できます。非同期テストを記述するには、testに渡す関数の前にasyncキーワードを付けます。例えば、同じfetchDataのシナリオは次のようにテストできます:

test('the data is peanut butter', async () => {
const data = await fetchData();
expect(data).toBe('peanut butter');
});

test('the fetch fails with an error', async () => {
expect.assertions(1);
try {
await fetchData();
} catch (error) {
expect(error).toMatch('error');
}
});

asyncawait.resolves.rejectsと組み合わせて使用できます。

test('the data is peanut butter', async () => {
await expect(fetchData()).resolves.toBe('peanut butter');
});

test('the fetch fails with an error', async () => {
await expect(fetchData()).rejects.toMatch('error');
});

この場合、asyncawaitは実質的にPromiseの例と同じロジックを簡潔に表現したシンタックスシュガーです。

注意

Promiseをreturnする(またはawaitする)ことを忘れないでください。return/awaitを省略すると、fetchDataから返されたPromiseが解決または拒否される前にテストが完了してしまいます。

Promiseが拒否されると予想する場合は、.catchメソッドを使用します。expect.assertionsを追加して、特定の数のアサーションが呼び出されることを確認してください。そうしないと、履行されたPromiseでもテストが失敗しません。

test('the fetch fails with an error', () => {
expect.assertions(1);
return fetchData().catch(error => expect(error).toMatch('error'));
});

コールバック

Promiseを使用しない場合、コールバックが利用できます。例えば、fetchDataがPromiseを返す代わりにコールバックを期待し、データ取得が完了するとcallback(null, data)を呼び出すとします。返されたデータが文字列'peanut butter'であることをテストしたい場合:

デフォルトでは、Jestのテストは実行の終端に達すると完了します。つまり次のテストは意図通りに動作しません

// Don't do this!
test('the data is peanut butter', () => {
function callback(error, data) {
if (error) {
throw error;
}
expect(data).toBe('peanut butter');
}

fetchData(callback);
});

問題は、コールバックが呼び出される前にfetchDataが完了した時点でテストが終了してしまうことです。

これを修正するtestの代替形式があります。引数のない関数にテストを記述する代わりに、doneという単一の引数を使用します。Jestはdoneコールバックが呼び出されるまで待機してからテストを終了します。

test('the data is peanut butter', done => {
function callback(error, data) {
if (error) {
done(error);
return;
}
try {
expect(data).toBe('peanut butter');
done();
} catch (error) {
done(error);
}
}

fetchData(callback);
});

done()が呼び出されない場合、テストは(タイムアウトエラーで)失敗します。これは期待通りの動作です。

expectステートメントが失敗するとエラーがスローされ、done()は呼び出されません。テストログで失敗理由を確認するには、expecttryブロックで囲み、catchブロックでエラーをdoneに渡す必要があります。そうしないと、expect(data)が受け取った値を示さない不明瞭なタイムアウトエラーが発生します。

注意

同じテスト関数がdone()コールバックを受け取りながらPromiseも返す場合、Jestはエラーをスローします。これはテストにおけるメモリリークを防ぐ予防措置です。

.resolves / .rejects

expectステートメントで.resolvesマッチャーを使用することも可能です。JestはPromiseが解決されるまで待機し、Promiseが拒否された場合は自動的にテストが失敗します。

test('the data is peanut butter', () => {
return expect(fetchData()).resolves.toBe('peanut butter');
});

アサーションをreturnすることを忘れないでください。returnステートメントを省略すると、fetchDataから返されたPromiseが解決される前にテストが完了し、then()のコールバックが実行される機会を失います。

Promiseが拒否されると予想する場合は、.rejectsマッチャーを使用します。これは.resolvesマッチャーと同様に動作します。Promiseが履行されると、テストは自動的に失敗します。

test('the fetch fails with an error', () => {
return expect(fetchData()).rejects.toMatch('error');
});

これらの形式のいずれも特に優れているわけではなく、コードベース全体や単一のファイル内で組み合わせて使用できます。どのスタイルがテストをよりシンプルにすると感じるかは、状況によって異なります。