Skip to content
This repository has been archived by the owner on Sep 8, 2022. It is now read-only.

Step5:テストを作成する

Tomohide Takao edited this page Apr 25, 2019 · 8 revisions

redux-plutoでは Service と Reducer のテスト作成を推薦しています。
今回はこれらのテストを作成していきましょう。

redux-pluto ではテストに jest を利用しています

Serviceテスト

テストファイルの作成

Service のテストでは、API 呼び出しが想定通りに行われているのかをテストします。
テストファイルは src/server/services/__test__ 配下に作成していきます。

service メソッドのテスト作成

src/server/services/__test__/Hello.read.test.ts

+import * as assert from "power-assert";
+import configs from "../../configs";
+import Hello from "../Hello";
+
+const getComments = require("../../../../spec/agreed/hello/getComments");
+
+test("Hello: read success", async () => {
+  const hello = new Hello(configs);
+  const result = await (hello.read as any)({}, {}, {});
+  assert.deepEqual(result, getComments.response.body.results);
+});

src/server/services/__test__/Hello.create.test.ts

+import * as assert from "power-assert";
+import configs from "../../configs";
+import Hello from "../Hello";
+
+test("Hello: create success", async () => {
+  const hello = new Hello(configs);
+  const body = { text: "hello" };
+  const result = await hello.create({}, {}, {}, body, {});
+  assert.ok(result.id);
+  assert.equal(result.text, body.text);
+});

利用module
power-asert
assert.ok: 引数に与えた値が true かどうかをテストする関数
assert.equal: 第1引数と第2引数の値が同値かテストする関数
assert.deepEqual: オブジェクトの最下位層まで一致しているかをテストする関数

テストを実行する

以下のコマンドを実行すると jest.config.js 内の testMatch にマッチする全てのテストが実行されます。

$ npm test

以下のようにテストが success すれば成功です!

Test Suites: 1 skipped, 24 passed, 24 of 25 total
Tests:       1 skipped, 38 passed, 39 total
Snapshots:   1 passed, 1 total
Time:        34.196s
Ran all test suites.

Reducerテスト

テストファイルの作成

Reducer のテストでは、dispatch された Action によって想定通りに state が更新されているのかをテストします。
Redux 配下には __test__ フォルダが 「src/shared/redux/__test__」、「src/shared/redux/modules/__test__」 の2種類あります。 前者には middleware などの利用が必要な redux 全体が絡むテスト、後者には module 内の reducer だけで完結するテストを書いていきます。

テストの中で module 内の INITIAL_STATE を利用したいので、まず初めに INITIAL_STATE を export しましょう。

src/shared/redux/modules/hello.js

-const INITIAL_STATE = {
+export const INITIAL_STATE = {

次に src/shared/redux/__test__ 配下にテストを作成していきましょう。

src/shared/redux/__tests__/hello.getComments.test.js

+import * as Fetchr from "fetchr"; // Fetcher の インポート
+import * as assert from "power-assert";
+import { FetchrStatic } from "./types";
+import { getComments, INITIAL_STATE } from "../modules/hello";
+import { createStore } from "./lib/storeUtils"; // store 作成のための util
+
+let needFailure: any = null; // リクエストを失敗させるかどうかのフラグ
+
+const comments = [{ id: "0001", text: "hello" }]; // API からの レスポンスデータ
+
+(Fetchr as FetchrStatic).registerService({
+  // reducer の中では Fetcher を利用して api 呼び出しを行っているため、Fetcher の登録を行う
+  name: "hello",
+  read(req, resource, params, config, cb) {
+    return needFailure
+      ? cb(new Error("failure")) // リクエスト失敗フラグが true の時には error を返す
+      : cb(needFailure, { comments }); // リクエスト失敗フラグが false の時には comments を返す
+  },
+});
+
+test("hello: getComments success", async () => {
+  // リクエスト成功時のテスト
+  const store = createStore({ cookie: {} });
+  assert.deepEqual(store.getState().app.hello, INITIAL_STATE); // 初期状態であることを確認
+
+  await store.dispatch(getComments());
+
+  // getComments を dispatch
+  assert.deepEqual(store.getState().app.hello, {
+    // state が 想定どうりに更新されているか確認
+    comments,
+    isVisible: true,
+    loaded: true,
+    loading: false,
+  });
+});
+
+test("hello: getComments failure", async done => {
+  needFailure = true; // リクエスト失敗フラグを立てる
+  const store = createStore({ cookie: {} });
+  assert.deepEqual(store.getState().app.hello, INITIAL_STATE);
+  try {
+    await store.dispatch(getComments());
+  } catch (_e) {
+    assert.deepEqual(store.getState().app.hello, {
+      comments: [],
+      isVisible: true,
+      error: true,
+      loading: false,
+      loaded: false,
+    });
+    done();
+  }
+});

src/shared/redux/__tests__/hello.postComment.test.js

+import * as Fetchr from "fetchr";
+import * as assert from "power-assert";
+import { FetchrStatic } from "./types";
+import { postComment, INITIAL_STATE } from "../modules/hello"; // postComment と INITIAL_STATE をインポート
+import { createStore } from "./lib/storeUtils";
+
+let needFailure: any = null;
+
+(Fetchr as FetchrStatic).registerService({
+  name: "hello",
+  create(req, resource, params, body, config, cb) {
+    return needFailure
+      ? cb(new Error("failure"))
+      : // リクエスト成功時には送られてきたテキストに id を採番したオブジェクトを返す
+        cb(needFailure, { id: "0001", text: body.text });
+  },
+});
+
+test("hello: postComment success", async () => {
+  const store = createStore({ cookie: {} });
+
+  assert.deepEqual(store.getState().app.hello, INITIAL_STATE);
+  const body = { text: "hello" };
+  await store.dispatch(postComment(body));
+  assert.deepEqual(store.getState().app.hello, {
+    comments: [{ id: "0001", text: body.text }],
+    isVisible: true,
+    loaded: true,
+    loading: false,
+  });
+});
+
+test("hello: postComments failure", async done => {
+  needFailure = true;
+  const store = createStore({ cookie: {} });
+  assert.deepEqual(store.getState().app.hello, INITIAL_STATE);
+  const body = { text: "hello" };
+  try {
+    await store.dispatch(postComment(body));
+  } catch (_e) {
+    assert.deepEqual(store.getState().app.hello, {
+      comments: [],
+      isVisible: true,
+      error: true,
+      loading: false,
+      loaded: false,
+    });
+    done();
+  }
+});

次に、src/shared/redux/modules/__test__ 配下に module に閉じたテストを作成していきます。

src/shared/redux/modules/tests/hello.test.ts

+import * as assert from "power-assert";
+import Immutable from "seamless-immutable";
+import reducer, { changeVisibility } from "../hello"; // reducer と テストしたい関数(changeVisibility) をインポート
+
+test("State: changeVisibility", done => {
+  const changeVisibilityAction = changeVisibility();
+  const INITIAL_STATE = Immutable({
+    // INITIAL_STATE を イミュータブルなオブジェクトとして設定
+    isVisible: false,
+    comments: [],
+    loading: true,
+    loaded: false,
+  });
+  let state = reducer(INITIAL_STATE as any, changeVisibilityAction as any);
+  // 設定した初期値に対して changeVisibilityAction を発火
+  assert.deepEqual(state, {
+    isVisible: true,
+    comments: [],
+    loading: true,
+    loaded: false,
+  });
+
+  // isVisible が true の状態でもう一度 changeVisibilityAction を発火
+  state = reducer(state, changeVisibilityAction as any);
+  assert.deepEqual(state, {
+    isVisible: false,
+    comments: [],
+    loading: true,
+    loaded: false,
+  });
+  done();
+});

テストを実行する

コンソールで以下のコマンドを実行し、再度テストを走らせてみましょう。

$ npm test

以下のようにテストが success すれば成功です!

Test Suites: 1 skipped, 27 passed, 27 of 28 total
Tests:       1 skipped, 43 passed, 44 total
Snapshots:   1 passed, 1 total
Time:        38.994s
Ran all test suites.

このSTEPのソースコード