【備忘録⑤】React & TypeScript & Webpack4 & Babel7 & dev-server の最小構成ボイラープレートの作成　-Jestの導入-



[前回の記事](https://qiita.com/Kento75/items/53e70aefb3fedc96f19e) で作成したボイラープレートにJestを導入する。

## 各種設定



### モジュールの追加


今回は Jest と Enzyme をインストールする。

```console
$ npm install -D jest ts-jest enzyme enzyme-to-json enzyme-adapter-react-16
```

<br>

TypeScript を使用するので以下もインストールする。

```console
$ npm install -D @types/jest @types/enzyme @types/enzyme-adapter-react-16
```



### Jest設定ファイルの作成

Jestの設定ファイルを追加する。

```javascript
// <プロジェクトルート>/jest.config.js

module.exports = {
  // 起点となるディレクトリを指定
  "roots": [
    "./src"
  ],
  // アセットの変換方法を指定
  "transform": {
    "^.+\\.tsx?$": "ts-jest"
  },
  // Jestで実行するテストコードの配置場所とテストコードの正規表現での指定
  // __tests__下の hoge.test.tsx または hoge.spec.tsx を対象とする
  "testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$",
  // テスト対象となる拡張子を列挙
  "moduleFileExtensions": [
    "ts",
    "tsx",
    "js",
    "jsx",
    "json",
    "node"
  ],
  // Enzymeの設定
  snapshotSerializers: ['enzyme-to-json/serializer'],
  // Enzymeの設定ファイル（自分で作成する）
  setupFilesAfterEnv: ['./test/setupEnzyme.ts'],
};
```

<br>

上記で設定した`__tests__`ディレクトリを作成しておく。

```console
$ mkdir ./src/__tests__
```



### Enzyme設定ファイルの作成

今回は、Enzymeを使用するので、こちらも設定ファイルを用意する。

```typescript
// <プロジェクトルート>/test/setupEnzyme.ts

import { configure } from 'enzyme';
import * as EnzymeAdapter from 'enzyme-adapter-react-16';
configure({ adapter: new EnzymeAdapter() });
```



### tsconfig.jsonを作成

TypeScriptの設定ファイルを用意する。

```json
{
  "compilerOptions": {
    "outDir": "./dist/",
    "sourceMap": true,
    "strictNullChecks": true,
    "strictPropertyInitialization": true,
    "noImplicitAny": true,
    "noImplicitReturns": true,
    "module": "es2015",  ← common.jsも設定できるが、静的解析の精度が落ちるのでes2015おすすめらしい
    "target": "es5",
    "jsx": "react",
    "esModuleInterop": true
  },
  "include": [
    "./src/**/*"
  ]
}

```



### package.jsonにJest実行用設定を追加

package.jsonにJest実行用設定を追加する。

```json
  "scripts": {
    "start": "webpack-dev-server --open",
    "build": "webpack",
    "build-prod": "webpack --mode=production",
    "storybook": "start-storybook -p 6006",
    "build-storybook": "build-storybook",
    "test": "jest"  ←ここ追加
  },
```



以上で、実行環境の構築は完了。



## 動作確認

### 確認用コンポーネント作成



前回作成したコンポーネントを使用する。

```tsx
// <プロジェクトルート>/src/components/Button/index.tsx

import * as React from 'react';

export interface IButtonProps {
  text: string;
  flag?: boolean;
  action(): void;
}

const Button = (props: IButtonProps) => {
  const { text, flag, action } = props;
  return (
    <React.Fragment>
      { flag && <p>{text}</p> }
      <button onClick={ action }>Button</button>
    </React.Fragment>
  );
};

export default Button;

```



### テストファイル作成

`__tests__`内にコンポーネントと同じディレクトリ構造を作成する。

```console
$ mkdir ./src/__tests__/components/Button

```



テストコードを作成する。

```tsx
// <プロジェクトルート>/src/__tests__/components/Button/index.test.tsx

import * as React from 'react';
import {shallow} from 'enzyme';
import Button from '../../../components/Button';

test('小コンポーネントが存在すること', () => {
  const wrapper = shallow(<Button text="ボタンです" flag={true} action={() => console.log("test")} />);

  expect(wrapper.find("button").length).toBe(1);
  expect(wrapper.find("p").length).toBe(1);

  expect(wrapper.find("p").text()).toEqual("ボタンです");

  expect(wrapper).toMatchSnapshot();
});

test('pコンポーネントが表示されないこと', () => {
  const wrapper = shallow(<Button text="ボタンです" flag={false} action={() => console.log("test")} />);

  expect(wrapper.find("button").length).toBe(1);
  expect(wrapper.find("p").length).toBe(0);

  expect(wrapper).toMatchSnapshot();
});

test('イベント発火時にコールバック関数が呼び出されること', () => {
  const Spy = jest.fn();
  const wrapper= shallow(
    <Button text="ボタンです" flag={true} action={Spy} />
  );

  wrapper.find('button').simulate('click');
  expect(Spy).toHaveBeenCalledWith();

  expect(wrapper).toMatchSnapshot();
});


```



### 実行


コンソールからテストコマンドを実行する。
実行すると、以下のようにテスト結果が出力される。

```console
$ npm run test

> react-ts-webpack@1.0.0 test /Users/kento/Programing/VScodeProjects/ts-react-sass-simple-boiler-v4
> jest

 PASS  src/__tests__/components/Button/index.test.tsx
  ✓ 小コンポーネントが存在すること (33ms)
  ✓ pコンポーネントが表示されないこと (3ms)
  ✓ イベント発火時にコールバック関数が呼び出されること (6ms)

Test Suites: 1 passed, 1 total
Tests:       3 passed, 3 total
Snapshots:   3 passed, 3 total
Time:        2.182s, estimated 5s
Ran all test suites.

```

<br>


テスト実行後、`__tests__`ディレクトリ内に、`__snapshots__`ディレクトリが作成され、中にスナップショットファイルが作成される。

```console
$ tree ./src/__tests__
./src/__tests__
└── components
    └── Button
        ├── __snapshots__
        │   └── index.test.tsx.snap
        └── index.test.tsx

```

<br>
`index.test.tsx.snap`の中身は以下の通り。

```jsx
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`pコンポーネントが表示されないこと 1`] = `
<Fragment>
  <button
    onClick={[Function]}
  >
    Button
  </button>
</Fragment>
`;

exports[`イベント発火時にコールバック関数が呼び出されること 1`] = `
<Fragment>
  <p>
    ボタンです
  </p>
  <button
    onClick={
      [MockFunction] {
        "calls": Array [
          Array [],
        ],
        "results": Array [
          Object {
            "type": "return",
            "value": undefined,
          },
        ],
      }
    }
  >
    Button
  </button>
</Fragment>
`;

exports[`小コンポーネントが存在すること 1`] = `
<Fragment>
  <p>
    ボタンです
  </p>
  <button
    onClick={[Function]}
  >
    Button
  </button>
</Fragment>
`;
```

<br>


以上で、Jestの導入は完了。
作成したボイラープレートは [こちら](https://github.com/Kento75/ts-react-sass-simple-boiler-v4)