掲示板アプリの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のテストについて扱いました。今回扱った非同期処理をテストするテクニックは、コールバック関数を渡すタイプの処理をテストする多くの場合で応用が可能です。テスト可能なコードを増やし、品質を高めていきましょう。
次回はアプリケーションをリリースする方法について紹介します。