既存のReact+ReduxプロジェクトにJest+Enzymeを導入しようとして躓いた点

# はじめに
フロントエンドのリファクタリングに伴い、Jest+Enzymeを用いたテストを導入することになった。
既存のプロジェクトは、React+Reduxで書かれており、複雑なWebpackの設定をしていたため、導入する際に躓いた点(発生したエラーと解決方法)をまとめる。

# エラー発生時のpackage.json
```
react: 16.4.1
react-dom: 16.4.1
redux: 3.6.0
react-redux: 5.0.6
@babel/core: 7.0.0
webpack: 3.10.0
webpack-dev-server: 2.11.1
```


# Jest + Enzymeの導入
### 基本的な流れ
基本的には以下の公式ドキュメント

- [Getting Started](https://jestjs.io/docs/ja/getting-started)
- [Using with webpack](https://jestjs.io/docs/ja/webpack)
- [Testing React Apps](https://jestjs.io/docs/ja/tutorial-react)

に従って導入していった。
その後、既存のコンポーネントに対して、簡単なテスト(子コンポーネントが存在するか確認するだけ)が通せるように変更を加えた。

### ドキュメントで苦戦したところ
基本的に書かれている通りに行えば上手くいった。
ドキュメント通りにやらなかったのでエラーが出た。

- **TypeError:Cannot read property 'current' of undefined**
 - [Getting Started](https://jestjs.io/docs/ja/getting-started)の_スナップショットテスト_で発生したエラー。
 - [原因] reactとreact-domのバージョンを16.9以上(ドキュメントでは最新にすべきだと書いてある)に上げる必要があるらしい。
 - [解決策] reactとreact-domのバージョンを上げれば動いた。  
<br />

- **typeerror helpers(...).ensure is not a function**
 - [Getting Started](https://jestjs.io/docs/ja/getting-started)の_モック、Enzyme および React 16 を使用したスナップショットテスト(Enzyme)_で発生したエラー。
 - [原因] ＠babel/coreのバージョンが低かった。
 - [解決策] @babel/coreのバージョンを上げれば動いた。  
<br />

- **Enzyme Internal Error: Enzyme expects an adapter to be configured, but found none.**
 - 同じく、[Getting Started](https://jestjs.io/docs/ja/getting-started)の_モック、Enzyme および React 16 を使用したスナップショットテスト(Enzyme)_で発生したエラー。
 - [原因] Enzymeを利用するには、Reactのバージョンに対応したアダプターをインストールする必要がある。
 - [解決策] 今回はReact16であるため、enzyme-adapter-react-16をインストールした。また、設定のためテストコードに以下を追記した。(参考: [React v16でAirbnbのenzymeを使う時の覚書](https://wp-kyoto.net/react-v16-run-enzyme/))

``` js:hogehoge.test.js
import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
configure({ adapter: new Adapter() });
```  
<br />

### 既存のコンポーネントに対する簡単なテスト(子コンポーネントが存在するか確認するだけ)で躓いたところ
- **Cannot find module 'hogehoge' from 'fugafuga'**
 - importあたりで躓いた。
 - [原因] Webpackでエイリアスを利用していて、その設定がJest側にないために失敗する。
 - [解決策] 以下のように、Jestの設定にWebpackのエイリアスと同じ設定をする必要がある。以下は「jest.config.js」を利用して設定を行う例だが、「package.json」に記載する方法もある。(参考:[Using with webpack](https://jestjs.io/docs/ja/webpack))

``` js:jest.config.js
module.exports = {
    moduleNameMapper: {
        // 「/src/components/**」に対し、「component」というエイリアスを設定する例
        "^component/(.*)": "<rootDir>/src/components/$1",
        // 「/src/containers/**」に対し、「container」というエイリアスを設定する例
        "^container/(.*)": "<rootDir>/src/containers/$1",
    },
};
```  
<br />

- **SyntaxError: Invalid or unexpected token**
 - cssやlessをimportしようとした際に発生する。
 - [原因] 静的アセットがテストで扱えずにコケる。(参考: [Using with webpack](https://jestjs.io/docs/ja/webpack))
 - [解決策] モックする必要がある。jest-css-modulesを用いて、以下のようにmoduleNameMapperに設定すると便利。jest-css-modulesはインストールする必要がある。(参考:[jestのテストでcss、scss、less絡みでSyntaxErrorが起きる場合の対処法](https://qiita.com/github0013@github/items/303a32d3037d322e67c0))

```bash
npm install react-css-modules
```

```js:jest.config.js
module.exports = {
    moduleNameMapper: {
        // 以下の行を追加(正規表現は適切なものに変える)
        "\\.(css|less|svg)$": "<rootDir>/node_modules/jest-css-modules",
    },
```  
<br />

- **Store周りの設定でエラー**
 - [原因] WebpackにStore周りの設定があり、その設定がJest側にないために失敗する。
 - [解決策] webpack.config.jsのpluginsに書かれていたDefinePluginの設定を、jest.config.jsのglobalsに転記した。  
<br />

- **Invariant Violation: Could not find "store" in either the context or props of "Connect(Login)".**
 - Reduxコンポーネントをテストする際に失敗する。
 - [原因] Jestのテストはアプリケーションから独立しており、storeは作成されないし、Reduxのセットアップもされないので、JestがReduxのstoreを探し出せずにエラーになる。
 - [解決策] 今回はインテグレーションテストを想定しているため、storeのモックを使ってテストを行う。redux-mock-storeをインストールし、テストコードに以下を追記。(参考: [Reduxコンポーネントをユニットテストする方法
](https://qiita.com/wabisuke2718/items/64be60c9232966fabd83))

>
``` react:hogehoge.test.js
import configureStore from 'redux-mock-store'
>
// create any initial state needed
const initialState = {}; 
// here it is possible to pass in any middleware if needed into //configureStore
const mockStore = configureStore();
let wrapper;
let store;
beforeEach(() => {
    //creates the store with any initial state or middleware needed  
    store = mockStore(initialState);
    wrapper = shallow(<Login store={store}/>);
})
```
(引用: [Reduxコンポーネントをユニットテストする方法](https://qiita.com/wabisuke2718/items/64be60c9232966fabd83))  

<br />

- **SyntaxError: Unexpected token import
   \> import Storage from 'react-native-storage';**
 - react-native-storageをincludeする際にエラーになる。
 - [原因] react-native-storageはES6で書かれているため、トランスパイルが必要になる。Jestのデフォルト設定では、「node_modules/」配下はトランスパイルの対象外になっているので、トランスパイルされずにエラーとなる。（今回はreact-native-storageだけだが、react-native関連のパッケージは同様のエラーが起きる。）
 - [解決策] jest.config.jsに以下を追記し、node_modulesはトランスパイルの対象から外したまま、react-native-storageだけ対象に入れるように、デフォルトの設定を上書きする。(参考: [Testing React Native Apps](https://jestjs.io/docs/ja/tutorial-react-native#transformignorepatterns-customization))

``` js:jest.config.js
    transformIgnorePatterns: [
        'node_modules/(?!(react-native-storage)/)'
    ],
```  
<br />

- **console.warn node_modules/react-native-storage/storage.js:28
    Data would be lost after reload cause there is no storageBackend specified!**
 - ローカルストレージを設定する必要がある。
 - これはまだ手を付けてない。