SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

基礎からはじめるReact入門

Reduxにおける同期的/非同期的なAction Creatorをテストする

基礎からはじめるReact入門 第12回

  • X ポスト
  • このエントリーをはてなブックマークに追加

掲示板アプリのAction Creatorをテストする

 それでは第9回で作成したRedux版掲示板アプリを題材に、実際のテスト内容を見てみましょう。画面の初期化を行うinitPosts関数をテストする方法について考えてみます。まず、製品コード側の実装はリスト12の通りです。

[リスト12]src/actions/posts.js
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引数として渡すdispatchjest.fn()で偽装することにします。また、テストの条件を固定するために、ApiClient.fetchPostsの戻り値も偽装する必要があります。

 これらをそれぞれ適用してテストコードを記述したものがリスト13です。

[リスト13]src/actions/posts.test.js
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のテストについて扱いました。今回扱った非同期処理をテストするテクニックは、コールバック関数を渡すタイプの処理をテストする多くの場合で応用が可能です。テスト可能なコードを増やし、品質を高めていきましょう。

 次回はアプリケーションをリリースする方法について紹介します。

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
基礎からはじめるReact入門連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト 中川幸哉(ナカガワユキヤ)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/11044 2018/09/07 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング