ダウンロード サンプルファイル (139.4 KB)
Reduxと自動テスト
さて、ReactとReduxで作られたアプリをテストしたいと思ったときに、どんなところをテストすると費用対効果が高いでしょうか。次のような機能はテストしづらいと筆者は考えています。
- UI
- 外部との通信を含む処理
UIは見た目について扱うため、テストがしづらい分野です。また、自動テストは常に同じ条件の下で同じ結果が出ることを確認するための仕組みである以上、常に同じ条件であることが保証できない外部との通信が含まれる処理は、自動テストを実施しづらいものになります。本来、JestはReactのUIテストを自動化する点では他のテスティングフレームワークに比べて秀でていますが、関数のテストに比べると実用的な運用をするための難易度が高いため、本記事では扱いません。また、非同期処理をテストするためのツールについてもJestはいくつかの解決策を用意していますが、これも1つ上の難易度になるため、本記事では扱いません。
さて、そういった事情を鑑みると、UIであるReactや、外部との通信が含まれることが多いAction Creatorのテストは少し難しいということになります。では、Reducerはどうでしょうか。Reducerの役割は「現在の状態とActionを与えられると、新しい状態を返す関数」でした。関数の中で同期的に実行された計算結果のみが返却され、非同期な処理が計算結果に影響することはありません。これは自動テストと相性が良さそうです。
ここからはReduxの中で最もテストしやすいモジュールである、Reducerのテストについて扱います。
Reducerをテストする
それでは、Reducerをテストする場合の考え方を紹介します。基本的にはどんな状態のときにどんなActionを与えたら、どんな状態になるのかを検証していく方針を取ります。
第8回で題材にした、カウンターアプリのReducerについて考えてみます。カウンターアプリのReducerは、リスト7のような実装になっていました。
count defaultState = { count: 0 };
const counter = (state = defaultState, action) => {
switch(action.type) {
case 'INCREMENT':
return {
count: state.count + action.payload.amount
};
case 'DECREMENT':
return {
count: state.count - action.payload.amount
};
case 'RESET':
return { ...defaultState }
default:
return state;
}
};
export default counter;
これに対するテストコードは、リスト8の形になりました。
import reducer from "./counter";
describe("type: INCREMENT", () => {
test("amountが1のときにcountが1増える", () => {
/* 条件 */
const state = { count: 3 }; // (1)
const action = { // (2)
type: "INCREMENT",
payload: {
amount: 1
}
};
/* テスト対象の処理を実行する */
const actual = reducer(state, action); // (3)
/* 処理結果が期待したものになっているかを検証する */
expect(actual).toEqual({ // (4)
count: 4
});
});
test("amountが5のときにcountが5増える", () => { ... });
});
describe("type: DECREMENT", () => {
test("amountが1のときにcountが1減る", () => { ... });
test("amountが5のときにcountが5減る", () => { ... });
});
describe("type: RESET", () => {
test("countが0になる", () => { ... });
});
まずはテストの条件として、更新前の状態(1)と発行されたAction(2)を用意します。これを(3)で実行し、(4)でtoEqualを用いて検証しています。Reducerが管理する状態はオブジェクトの形になっていることがほとんどなので、値の評価にはtoEqualを使うことが多くなります。管理上のテクニックとしては、Actionのtypeごとにdescribeでまとめて、payloadの違いによってテストケースを分けるスタイルを採用してみました。
どんな複雑なReducerであっても、基本的な方針は同様です。stateとactionを用意して、更新後のstateが期待したものになっているかを検証します。
まとめ
テスティングフレームワークJestの紹介と、Reducerのテスト作成方法について学んできました。テストコードは作者がどんな意図でロジックを作成し、どんな動作確認を行ったかをコードの形で残せるので、チーム開発においてとても重要なものです。積極的にテストコードを作成して、アプリの品質を高めていきましょう。
次回はもう一歩踏み込んで、Action Creatorや非同期処理のテストについて解説します。
この記事は参考になりましたか?
既に会員の方はを行ってください。
- この記事の著者
-
WINGSプロジェクト 中川 幸哉(ナカガワ ユキヤ)
<WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。 2026年時点での登録メンバは約50名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書、記事多数。 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本格入門」「これからはじめるLaravel実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数。
※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です
この記事は参考になりましたか?
この記事をシェア
