$shibayu36->blog;

株式会社はてなでエンジニアをしています。プログラミングや読書のことなどについて書いています。

React + Apolloを使ったコンポーネントのテストをする

Reactを使ったコンポーネントのテストのやり方を知らなかったので、やってみた内容をメモ。hatena/go-Intern-Diaryをお試し環境として利用した。結果は https://github.com/shibayu36/go-Intern-Diary/pull/5/files

テスト概要 – Reactを最初の資料としながら色々調査していった。

どのライブラリを使うか

  • Jest: テストランナーやアサーションライブラリ、テスト便利グッズ用途として。Reactの公式でもおすすめされているので利用する。
  • enzyme: Reactコンポーネントをテストするための便利グッズ用途として。
    • 公式では@testing-library/reactがおすすめされている参考ようだが、renderメソッドの返り値でgetByTextとかDOM操作用の便利関数が返ってくるモデルのインターフェースが気に入らなかったのでenzymeにした
  • @apollo/react-testing: Apolloのテスト用の便利グッズとして
  • @testing-library/react: enzymeを使ったとしても、act()などの関数が必要になることがあるため

テストを書くまでの手順

以下の手順で始められる。

  • 必要なpackageをインストールする
  • Jestのセットアップをする
  • テストを書く

必要なpackageをインストールする

レポジトリ内の package.json を参考に。TypeScriptで作っているので型のためのpackageも入れる必要あり。

yarn add -D @apollo/react-testing @testing-library/react enzyme enzyme-adapter-react-16 jest ts-jest

# 型
yarn add -D @types/enzyme @types/enzyme-adapter-react-16 @types/jest

# 使っても使わなくても良いもの
yarn add -D waait

Jestのセットアップをする

jest.config.jsの用意と、enzymeのためにsetupTests.tsを書けばOK。

jest.config.js。TypeScriptをテストで使うために変換の設定と、テスト時の最初に読み込むファイル(setupTests.ts)の設定をしている。

module.exports = {
  preset: "ts-jest",
  verbose: true,
  roots: ["<rootDir>/ui/src"],
  transform: {
    "^.+\\.tsx?$": "ts-jest"
  },
  setupFilesAfterEnv: ["<rootDir>/ui/src/setupTests.ts"],
  globals: {
    "ts-jest": {
      tsConfig: "ui/tsconfig.json"
    }
  }
};

ui/src/setupTests.ts。enzymeのセットアップのために呼びたいコードを書いている。

import { configure } from "enzyme";
import Adapter from "enzyme-adapter-react-16";

configure({ adapter: new Adapter() });

テストを書く

あとは自分の書きたいテストを書くだけ。

ブログ一覧ページのテストを簡単に書いたものは以下の通り。

ui/src/pages/__tests__/diaries.test.tsx

import React from 'react';
import wait from 'waait';
import { mount } from 'enzyme';
import { getMyDiariesQuery, Diaries } from '../diaries';
import { MockedProvider } from '@apollo/react-testing';
import { act } from '@testing-library/react';
import { BrowserRouter as Router } from 'react-router-dom';

describe('<Diaries />', () => {
  it('正常にレンダリングできる', async () => {
    const mocks = [
      {
        request: {
          query: getMyDiariesQuery
        },
        result: {
          data: {
            visitor: {
              id: '1',
              name: 'shibayu36',
              diaries: [
                {
                  id: '2',
                  name: 'shibayu36の日記'
                },
                {
                  id: '3',
                  name: 'shibayu36のブログ'
                }
              ]
            }
          }
        }
      }
    ];

    const wrapper = mount(
      <MockedProvider mocks={mocks} addTypename={false}>
        <Router>
          <Diaries />
        </Router>
      </MockedProvider>
    );

    await act(async () => {
      await wait(0);
    });
    wrapper.update();

    expect(wrapper.find('h1').text()).toBe('shibayu36のダイアリー一覧');

    const diaries = wrapper.find('Link');
    expect(diaries).toHaveLength(3);
    expect(diaries.at(0).prop('to')).toBe('/diaries/2');
    expect(diaries.at(0).find('p').text()).toBe('shibayu36の日記');
    expect(diaries.at(1).prop('to')).toBe('/diaries/3');
    expect(diaries.at(1).find('p').text()).toBe("shibayu36のブログ");
  });
});

ポイントとしては

これでなんとなくテストをするイメージを掴むことができた。

まとめ

今回はReact + Apolloを使ったコンポーネントのテストをする方法について簡単な実践をして、分かったことを書いてみた。さらにいろんなテストを書くためには以下のような資料を読むと良いだろう。