ReactをJest+enzymeでテストする

#はじめに
初投稿です。
とりあえず忘備録として投稿していこうかなと思います。

とりあえず記念すべき最初の記事として選んだ内容がReactのテスト記事です。
3つくらいのサービスでReactを書いてきたのですが、テストを全く書いてこなかったのでそろそろ。。。ということです。（テスト大事ですね。。）
ちなみに基本Railsアプリケーションに載せる構成だったのですが、そちらもサーバーサイドもテストはおろそかにしていたので、今のところそこまで深い理解はないです。
書いていきながらどんなテストをしていけば良いかなーとかを学んでいこうかと。

ということで本題。

#前提条件
今回の記事はテストを導入するまでの設定に関するものです
基本的にふんわりな理解のところが多いのでそこあはどうか暖かい目で。
アプリケーション構成としてはRailsにReactを後から乗せた形になってます。
Reactが動く状態を前提としています。
cssは`css-loader`でmodule化してあります。
es6をwebpackを使ってbabel変換しているのでそれに必要なものはインストールしてある状態です。

#使用ライブラリ
Node 12.5.0
React 16.8.6
webpack 4.35.2
Jest 24.9.0
enzyme 3.10.0


#ディレクトリ構成
```
front
├── src
│    └── __test__
│            ├── __mock__
│            └── test.spec.js
│
├── assets
├── javascripts
├── stylesheets
├── babel.config.js
├── package.lock.json
├── package.json
├── webpack.dev.js
└── webpack.prod.js

```

javascripts以下にcomponentやentryファイルを格納してあります・
stylesheets以下.scssファイル
assets以下には画像ファイルなど

#テスト対象コンポーネント

```react:test.rb
import React from 'react'
import { render } from 'react-dom'

import testStyles from '../../stylesheets/components/test'

class TestComponent extends React.Component {
  constructor(props) {
    super(props)
  }

  render() {
    return(
      <div class={testStyles.testdiv}>React Test</div>
    )
  }
}

export default TestComponent
```

今回のテストするコンポーネントの例です。(今回はテストまで書きません)


#必要なライブラリのインストール
jest 

```sh
npm install i --save-dev jest 
```

enzyme

```sh
npm i --save-dev enzyme enzyme-adapter-react-16
```

同時にenzeymeの設定ファイルも作成しておきます

```javascript:setupTest.js
import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';

Enzyme.configure({ adapter: new Adapter() });
```



#設定をする
とりあえず挿入するにあたり様々なエラーに遭遇しました。
主に下の3つに遭遇したのでそれに対応するライブラリを書いていこうかと思います。
設定ファイルの最終系は最後に。

まず1つ目
import構文が使えない
Babelで変換している場合はそのままでは読み込めない。

```
/Users/UserName/Application/front/src/__test__/setupTests.js:1
({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){import Enzyme from 'enzyme';
                                                                                                    ^^^^^^
SyntaxError: Unexpected identifier
```

ここで使うのが`babel-jest`

```sh
npm i --dev babel-jest
```

それとbabelのconfigファイルも作成する

```javascript:bable.config.js
module.exports = {
  presets: [ ['@babel/preset-env'], ['@babel/preset-react'] ]
};
```
これはroot以下に置いておけば勝手に読み込まれます。

以上の設定をすれば読み込みができる。

2つ目
画像ファイルが読み込めない(svg,png,jpg etc.)

```
"({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){<?xml version="1.0" encoding="UTF-8"?>                                                                                    
SyntaxError: Unexpected token
```

公式には
> 静的アセットの管理
次は、スタイルシートや画像などのアセットを簡潔に管理できるようにJestを設定しましょう。 通常、これらのファイルはテストでは特に扱いづらいので、問題がないようにモックします。 

こんな感じで書いてあります。
とりあえずテストでは使わないし扱いづらいからmockしてしまいましょうということです。

mockするためのファイルを作成する。

```javascript:__mocks__/fileMock.js
module.exports = 'test-file-stub';
```

これを設定ファイルで指定してあげることで静的アセットをmockすることができます。

3つ目
module化したスタイルシートが読み込めない

```
Error: Could not parse CSS stylesheet
```

これに関しても上の画像ファイルと同じでmockをしてしまいましょう。

そこで使うのが`identity-obj-proxy

```
npm i --dev identity-obj-proxy
```

これも設定ファイル内で指定して使ってあげることでcssファイルをmockすることができます。

ちなみにmodule化してないcssファイルの場合は下記のファイルを作成して指定してあげることでmockができるみたいです。


```javascript:__mocks__/styleMock.js
module.exports = {};
```

クラス名を参照するかどうかで設定が変わりますが、クラス名参照のためには前者の方法で行わないとダメみたいです。

>CSSモジュールを利用している場合はクラス名参照のためのプロキシをモックした方が良いでしょう。

と公式にあります。

#設定ファイル

設定ファイルの最終系
かなり最低限の設定なのでもしかしたらテストしていくうちに必要な記載が出てくるかもしれないです。

```javascript:package.json
  "jest": {
    //セットアップファイルの読み込み
    //今回はenzymeの設定です
    "setupFilesAfterEnv": [
      "<rootDir>/src/__test__/setupTests.js"
    ],
　　//node_modules以下のファイルはテストしないので無視するファイルとして指定
    "transformIgnorePatterns": [
      "/node_modules/.+.(js|jsx|ts|tsx)$"
    ],
    //こちらが静的アセット達のmockの設定
　　//それぞれ作成したファイル、ライブラリを指定してあげる
　　//ファイル構成によりますが、今回はstylesheets以下にファイルをまとめてるのでそれ以下のものはmock
    "moduleNameMapper": {
      ".(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/src/__test__/__mocks__/fileMock.js",
      "stylesheets/.+": "identity-obj-proxy"
    },
    //babelで変換したコードを読み込むための設定
　　//babel-jestを指定
    "transform": {
      "^.+.(js|jsx|ts|tsx)$": "<rootDir>/node_modules/babel-jest"
    }
  }
```

以上が導入するまでに出てきたエラーとそれに対応する対処の方法でした。


#参考リンク
[Jest](https://jestjs.io/ja/)
[Using with webpack](https://jestjs.io/docs/ja/webpack#webpack2%E3%81%A8%E4%BD%BF%E7%94%A8%E3%81%99%E3%82%8B%E5%A0%B4%E5%90%88)