jest/enzyme導入

# Jestとは
jasmineの後継テスティングフレームワーク。
CRAに予め組み込まれてはいるが、最新のバージョンではないため、明示的にインストールする。

```linux-config
npm i --save-dev jest
```

`npm (run) test`だとCRAにデフォルトで組み込まれているバージョンのjestを使用してしまうので、明示的に`jest`コマンドを実行するよう書き換える。

```package.json
"scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "jest",  //jestを実行するよう書き換える
    "eject": "react-scripts eject"
  },
```

# Enzymeとは
Airbnb社が提供しているReactのコンポーネントテストツール。コンポーネントを`shallow`(浅いコピー)、`mount`(ディープコピー)して非破壊のままUIのテストができる。

## Enzymeのインストール
enzymeと使用しているReactのバージョンに合わせたアダプタをインストールする。

```linux-config
npm i --save-dev enzyme enzyme-adapter-react-16
```

`setupTests.js`を作成し、アダプタを初期化する処理を記述する。

```setupTests.js
import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import '@babel/polyfill';

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

## babelを使用するための準備をする
ReactJS、VueJSなどで使われている`JSX`をjestが解釈するためにbabelが必要。

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

```linux-config
npm i --save-dev @babel/polyfill @babel/preset-env @babel/preset-react babel-core babel-jest
```

### 設定を記述する

```babel.config.js
module.exports = {
  "presets": ['@babel/preset-env', '@babel/preset-react'],
  "plugins": [
    "@babel/plugin-proposal-class-properties"
  ]
}
```

## package.jsonにJestのコンフィグを記述する
### テストを起動する際に`setupTests.js`を読み込む設定

```package.json
{
    "jest": {
        "setupFilesAfterEnv": [
          "<rootDir>/src/setupTests.js"
        ]
     }
}
```
### テストの対象からCSSを除外する
この設定を書かないとCSSもjsとして認識され、実行エラーとなる。

```package.json
{
    "jest":{
        //ts・jsなどはbabel-jestで処理する
        //静的ファイルはstyleMock.jsで処理する
        "transform": {
          "^.+\\.(ts|tsx|js)?$": "babel-jest",
          "^.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$": "identity-obj-proxy"
        },
       //node_modulesは無視する
        "transformIgnorePatterns": [
          "/node_modules/"
        ],
        "moduleNameMapper": {
          "^.+\\.(css|less)$": "<rootDir>/src/__mocks__/styleMock.js"
        }
    }
}
```

### styleMock.jsでは何も返さない処理を記述する。

```styleMock.js
module.exports = {};
```

# コンポーネントテストの書き方
## 要素が正しくレンダリングされているかどうかの確認
```Sample.spec.js
(import宣言省略)

//test起動時に毎回行う処理
beforeEach(()=>{
  ....
})

describe('Sampleコンポーネントのテスト',()=>{
  it('FormGroupが存在すること',() => {
    const wrapper = mount(
      <Provider store={store}>
        <Sample/>
      </Provider>
    );
    //FormGroupの要素が１つレンダリングされること
    expect(wrapper.find(FormGroup).length).toBe(1);
  })
})

```

## クラスの中のメソッドの動作の確認
```javascript
it('maxLengthの最大値を超えた場合のバリデーションができること',() => {
    
    const wrapper = mount(
      <Sample/>
);
   //stateに値をセットする
    wrapper.setState({
      value: {
        length:9
      }
    });
    
    let result = wrapper.instance().
    expect(result).toBe('error');
  });
```

## stateの中身を検証する
```javascript
it("stateに初期値がセットされていること", () => {
   //stateの中身を監視する 
   const setStateSpy = jest.spyOn(*****.prototype, 'setState');
    
    const props = {
      data: {
        attr:'fugafuga'
      },
      name:'hogehoge',
     }
    };
    const wrapper = mount(
      <Provider store={store}>
        <Sample {...props} />
      </Provider>
    );
    //stateの中身を検証する
    expect(setStateSpy).toHaveBeenCalledWith({
      name:'hogehoge',
      attr:'fugafuga'
    })
  });
```

# カバレッジの出し方

・jestがグローバルインストールされている場合

```
$ jest --collectCoverage
```

# 参考リンク
- [Jest公式ドキュメント](https://jestjs.io/ja/)
- [Enzyme公式ドキュメント](https://airbnb.io/enzyme/)
- [Jest + enzymeで行うReactのユニットテスト（単体テスト）について](https://mae.chab.in/archives/60066)
- [How to exclude CSS module files from jest test suites?
](https://stackoverflow.com/questions/46177148/how-to-exclude-css-module-files-from-jest-test-suites)
- [babelの実行時に「Support for the experimental syntax 'classProperties' isn't currently enabled」と言われる場合の対応方法](https://qiita.com/taki3/items/80935b3eeb8dbb58f074)
- [Testing with Jest: SyntaxError: Unexpected identifier](https://github.com/lukaszflorczak/vue-agile/issues/94)
- [Jest CLI Options](https://jestjs.io/docs/ja/cli#--coverageboolean)