はじめに
Jestの基礎はざっくり理解したので、非同期テスト方法の方法ついて学びたいと思いました。
Jestの基礎がある程度分かっている方が対象になります。
この記事で学べること
async/await
を使用した非同期の場合や、コールバック関数を使用した場合のテストケースの作成方法について学べます。
動作環境
- OS:macOS Sequoia 15.1.1
- Node.js:22.6.0
- npm:10.8.2
- Jest:29.7.0
インストール方法
Jestの環境構築についてはこちらをご確認ください。
基本的な使い方
非同期のテストの場合はtest
関数にasync/await
を付ける必要がある
Promiseオブジェクトを直接返す場合はtest
関数にasync/await
を付ける必要はありません。
async/await
を付けると、test
関数は非同期関数として扱われます。
// 引数によってPromissのresolveかrejectを返す
function fetchData(success = true) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (success) {
resolve("peanut butter");
} else {
reject("error");
}
}, 100);
});
}
// Promiseオブジェクトをreturnしていればasync,awaitは必要無い
test("peanut butterが返ってくる事を確認", () => {
return fetchData().then((data) => {
expect(data).toBe("peanut butter");
});
});
// async/awaitを付けていないと、setTimeoutが終わる前にテストが終了してしまうのでエラー
test("peanut butterが返ってくる事を確認",() => {
const data = fetchData();
expect(data).toBe("peanut butter");
});
test("エラーが返ってくる事を確認", async () => {
expect.assertions(1); // このテストでは1つのexpectが実行されることを宣言
try {
await fetchData(false);
} catch (error) {
expect(typeof error).toBe("string");
}
});
reject
のテストをする場合は、expect.assertions
を必ず指定する必要があります。
try/catch
を使用してテストする場合はexpect.assertions
を書かないとexpect
が一度も実行されずテストが通過してしまう事があります。
非同期関数用に用意されたマッチャーを使用すれば、以下のように書くこともできます。
この場合はPromiseオブジェクトをreturn
するのでasync/await
は必要ないです。
// .resolvesマッチャーを使ったテスト(Promiseの成功を確認)
test("peanut butterが返ってくる事を確認(.resolves)", () => {
return expect(fetchData()).resolves.toBe("peanut butter");
});
// .rejectsマッチャーを使ったテスト(Promiseの失敗を確認)
test("エラーが返ってくる事を確認(.rejects)", () => {
return expect(fetchData(false)).rejects.toMatch("error");
});
try/catch
を使った場合は複数のアサーションを使う場合に使用し、1つのアサーションの場合は非同期関数のマッチャーを使用したら良いかと思います。
コールバック関数のテスト方法
// コールバック関数を受け取る関数
function fetchDataWithCallback(callback) {
setTimeout(() => {
callback("peanut butter",);
}, 100);
}
test("コールバック関数のテスト", (done) => {
function callback(data) {
try {
expect(data).toBe("peanut butter");
done(); // テスト成功時に完了を通知
} catch (error) {
done(error); // 失敗したらエラーを渡して失敗を通知
}
}
fetchDataWithCallback(callback);
});
コールバック関数のテストはdone
を付けないとfetchDataWithCallback
が呼ばれた時点でテストが終了してしまいcallback
が実行されなくなってしまう。
doneの通知を見てみる
以下で意図的に失敗させてdoneはなぜ必要なのか確認します。
test("コールバック関数のテスト", (done) => {
function callback(data) {
try {
expect(data).toBe("wrong value"); // 意図的に失敗させる
done(); // テスト成功時に完了を通知
} catch (error) {
done(error); // 失敗したらエラーを渡して失敗を通知
}
}
fetchDataWithCallback(callback);
});
ログの内容
> test
> jest throw.test.js
FAIL ./throw.test.js
✕ コールバック関数のテスト (116 ms)
● コールバック関数のテスト
expect(received).toBe(expected) // Object.is equality
Expected: "wrong value"
Received: "peanut butter"
10 | try {
11 | // expect(data).toBe("peanut butter");
> 12 | expect(data).toBe("wrong value"); // 意図的に失敗させる
| ^
13 | done(); // テスト成功時に完了を通知
14 | } catch (error) {
15 | done(error); // 失敗したらエラーを渡して失敗を通知
at toBe (throw.test.js:12:20)
at Timeout.callback [as _onTimeout] (throw.test.js:4:5)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 total
Snapshots: 0 total
Time: 0.902 s, estimated 1 s
done(error)
を指定している事で、Expected
とReceived
が表示されています。
done(error)
がないと引数の値は表示されない為、失敗時にエラーを渡してくれるdone(error)
は必ず指定するようにしましょう。
ハマりやすい点・注意点
コールバック関数の場合にdoneを付け忘れてしまったり、try/catch
を使用したreject
のテスト時にexpect.assertions
を付け忘れてしまうとテストがそのまま通ってしまうのでそこだけ注意が必要だと感じました。
まとめ
今回は非同期のテストケース作成方法について学びました。
基本の書き方は普通のテストケースとほぼ変わらず、Promiseオブジェクトを返す場合と返さない場合で少し書き方が変わる事と、コールバック関数の場合はdone
を付けると覚えておけば何とかやっていけそうな気がしました。