はじめに
各コンポーネントに、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>
を呼び出せるよう、別ファイルに切り出しておくことで、テストが行いやすくなる