掲示板アプリのAction Creatorをテストする
それでは第9回で作成したRedux版掲示板アプリを題材に、実際のテスト内容を見てみましょう。画面の初期化を行うinitPosts関数をテストする方法について考えてみます。まず、製品コード側の実装はリスト12の通りです。
import ApiClient from "../api/ApiClient";
export async function initPosts(dispatch) {
try {
const posts = await ApiClient.fetchPosts();
dispatch(updatePosts(posts)); // (1)
} catch(e) {
dispatch(initFailed(e));
}
}
async/awaitを用いるスタイルのAction Creatorですね。さて、今回検査したいのは(1)でdispatchを実行する際の引数です。これを検査するためには、initPostsの第1引数として渡すdispatchをjest.fn()で偽装することにします。また、テストの条件を固定するために、ApiClient.fetchPostsの戻り値も偽装する必要があります。
これらをそれぞれ適用してテストコードを記述したものがリスト13です。
import * as actions from "./posts";
import ApiClient from "../api/ApiClient";
describe("initPosts()", () => {
test("APIから取得したデータでActionを作成できる", async () => { // (4)
expect.assertions(2);
/* 条件 */
// APIクライアントの動作を偽装する
ApiClient.fetchPosts = () => Promise.resolve([ // (1)
{ id: 1, name: "なかがわ", age: "thirties", body: "なかがわです" },
{ id: 2, name: "たなか", age: "fourties", body: "たなかです" },
{ id: 3, name: "すずき", age: "teen", body: "すずきです" },
]);
/* テスト対象の処理を実行する */
// dispatchを偽装する
const mockDispatch = jest.fn(); // (2)
await actions.initPosts(mockDispatch); // (3)
/* 処理結果が期待したものになっているかを検証する */
// dispatchは1回だけ呼ばれている
expect(mockDispatch.mock.calls.length).toBe(1); // (5)
// dispatchに渡されたActionは偽装された値と同じものを含んでいる
expect(mockDispatch.mock.calls[0][0]).toEqual({ // (6)
type: "UPDATE_POSTS",
payload: {
posts: [
{ id: 1, name: "なかがわ", age: "thirties", body: "なかがわです" },
{ id: 2, name: "たなか", age: "fourties", body: "たなかです" },
{ id: 3, name: "すずき", age: "teen", body: "すずきです" },
]
}
});
});
});
まずは通信内容を固定するために、ApiClient.fetchPostsの処理内容を書き換えます(1)。今回はApiClientのプロパティとしてfetchPostsが直接存在していたため、代入で差し替えることができました。ただ、もしApiClientがクラスだった場合など、外側から差し替えるのが困難な場合には、ApiClientのインスタンスをinitPostsの引数として渡す方法も検討するべきでしょう。
次に、dispatch関数の代わりとなるモック関数を生成して(2)、initPostsにそれを渡す形で実行します(3)。このとき、initPostsはPromiseを返す非同期処理ではあるものの、テストとしては同期的な記述をしたいので、async/awaitを使います。そのため、このテストケースの実行関数はasync関数として定義されています(4)。
その後、モック関数から呼び出し回数(5)と引数の内容(6)を引き出して、意図したActionがdispatchされたことを確認します。(6)では(1)で定義したデータと同じものがpayloadの一部として定義しています。これらが同じものであることを確認することで、処理が正しく行われたことを検証する形を取りました。
これで非同期なAction Creatorのテストができました。
まとめ
前回に引き続き、Reduxのテストについて扱いました。今回扱った非同期処理をテストするテクニックは、コールバック関数を渡すタイプの処理をテストする多くの場合で応用が可能です。テスト可能なコードを増やし、品質を高めていきましょう。
次回はアプリケーションをリリースする方法について紹介します。
