本文へスキップ
バージョン: 29.7

非同期コードのテスト

JavaScriptでは、非同期的に実行されるコードが一般的です。非同期的に実行されるコードがある場合、Jestはテスト対象のコードが完了するまで待機する必要があります。その後、次のテストに進むことができます。Jestには、これに対処するためのいくつかの方法があります。

Promise

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

たとえば、`fetchData`が文字列`'peanut butter'`に解決されるはずのPromiseを返すものとします。それを次のようにテストできます。

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

Async/Await

あるいは、テストで`async`と`await`を使用することもできます。非同期テストを作成するには、`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');
}
});

`async`と`await`を`.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');
});

これらの場合、`async`と`await`は、Promiseの例で使用されているものと同じロジックに対する、事実上の構文糖です。

注意

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

Promiseが拒否されることを期待する場合は、`.catch`メソッドを使用します。特定数のassertionが呼び出されたことを検証するために、`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()`は呼び出されません。テストログに失敗した理由を確認したい場合は、`expect`を`try`ブロックでラップし、`catch`ブロックでエラーを`done`に渡す必要があります。そうしないと、`expect(data)`で受信した値が表示されない不透明なタイムアウトエラーが発生します。

注意

同じテスト関数に`done()`コールバックを渡してPromiseを返す場合、Jestはエラーをスローします。これは、テストのメモリリークを防ぐための予防措置として行われます。

.resolves / .rejects

expect文で`.resolves` matcherを使用することもできます。JestはそのPromiseが解決されるまで待ちます。Promiseが拒否された場合、テストは自動的に失敗します。

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

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

Promiseが拒否されることを期待する場合は、`.rejects` matcherを使用します。これは`.resolves` matcherと同様に機能します。Promiseが正常に完了した場合、テストは自動的に失敗します。

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

これらの形式のいずれも、特に他の形式よりも優れているわけではなく、コードベース全体または単一ファイル内で自由に組み合わせることができます。テストをよりシンプルにするスタイルに応じて選択できます。