React

【テストしやすいRedux】を切り出す

React
この記事は約4分で読めます。

はじめに

各コンポーネントに、ReduxのStoreで管理する状態を渡すとき<Provider>を使用しますよね。

これをルート直下でindex.jsとかApp.jsとかにガッツリ書かずにファイル分割させておくだけで、Jestのテストが書きやすくなります。

よくある書き方

index.js内など、コンポーネントツリーのトップレベルで<Provider />を記述し、<App />以下のコンポーネントがStore内の状態にアクセスできるようにする。

/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import reducers from 'reducers';

import App from 'src/App';

ReactDom.render(
  <Provider store={createStore(reducers, {})}>
    <App />
  </Provider>,
  document.querySelector('#app')
);

Jestによるテストで起こる問題

Jestでコンポーネントのテストをする場合、通常コンポーネントは単体で呼び出され、DOMツリーを構成しません。
したがって、<Provider>と繋がってない状態のコンポーネントになります。
connect()などでstore情報を使用するコンポーネントだと、エラーになります)

どういうことかというと、

私たちがReactアプリケーションを動作させる場合、基本的にブラウザを通してトップレベルの<App />からツリー状にDOMが構成されます。

一方で、Jestなどテストツールにおいては、コンポーネントはブラウザを通してマウントされるわけではなく、いわば仮想的に単体でマウントした状態になります。

<Provider>でラップされていない状態でコンポーネントがマウントするので、そのコンポーネント内でStoreの情報を使用していると当然テストは落ちてしまいます。

<Provider>を切り出す

<Provider>は別ファイル(ここではRoot.js)に切り出し、テストでも自由に呼び出して使えるようにします。

/Root.js

import React from 'react';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import reducers from 'reducers';

export default ({ children, initialState = {} }) => (
  <Provider store={createStore(reducers, initialState)}>
    {children}
  </Provider>
);

つまり、index.jsは以下のように変更されます。

/index.js

import React from 'react';
import ReactDOM from 'react-dom';

import Root from 'Root';
import App from 'src/App';

ReactDom.render(
  <Root>
    <App />
  </Root>,
  document.querySelector('#app')
);

まとめ

  • Jestではコンポーネントが単体で呼び出されるため、<Provider>と繋がっていない
  • つまりStoreの変数が参照できずエラーになってしまう
  • いつでも<Provider>を呼び出せるよう、別ファイルに切り出しておくことで、テストが行いやすくなる