【ReactNative, TypeScript】既存のアプリにJest, Enzyme, Huskyを導入する方法

# 目標

既存のReactNativeで開発したアプリに、Jest, Enzymeを導入し、「多面！フラッシュ暗算」というテキストが正しく表示できるかをテストする。

https://github.com/wafuwafu13/flash-mental-arithmetic

```tsx:HomeTitle.tsx
import React from 'react';
import { StyleSheet, Text } from 'react-native';
import {
    widthPercentageToDP as wp,
    heightPercentageToDP as hp
} from 'react-native-responsive-screen';

const HomeTitle: React.FC = () => {
    return <Text style={styles.title}>多面！フラッシュ暗算</Text>;
};

export default HomeTitle;

const styles = StyleSheet.create({
    title: {
        color: '#fff',
        fontSize: wp('7%')
    }
});
```

<img width="320" alt="Simulator Screen Shot - iPhone SE - 2020-07-15 at 14.34.08.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/402245/9f88024c-ccbe-94e8-a06f-a80c933c31cf.png">

初期状態は、TypeScriptで開発できる環境は整っているが、Jest, Enzymeはインストールすらされていない。

```json:package.json
"scripts": {
    "start": "expo start",
    "android": "expo start --android",
    "ios": "expo start --ios",
    "web": "expo start --web",
    "eject": "expo eject",
    "prettier": "npx prettier --write '**/*.{ts,tsx}'"
 },
```

# Jestの導入

[参考]
https://reactnative.dev/blog/2018/05/07/using-typescript-with-react-native

公式通りに``ts-jest``をインストールし、``package.json``にJestの設定を書いていく。

```
$ npm i --save-dev ts-jest 
```

```package.json
"jest": {
    "preset": "react-native",
    "moduleFileExtensions": [
      "ts",
      "tsx",
      "js"
    ],
    "transform": {
      "^.+\\.(js)$": "<rootDir>/node_modules/babel-jest",
      "\\.(ts|tsx)$": "<rootDir>/node_modules/ts-jest/preprocessor.js"
    },
    "testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$",
    "testPathIgnorePatterns": [
      "\\.snap$",
      "<rootDir>/node_modules/"
    ],
    "cacheDirectory": ".jest/cache"
  },
```

次も公式通りに必要なパッケージをインストールする。

```
$ npm i --save-dev @types/jest @types/react-test-renderer
```

ここで試しに``jest``コマンドを打ってテストが走るかどうかを確認する。

<img width="711" alt="スクリーンショット 2020-07-15 12.20.41.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/402245/181e674c-3501-a41a-59f9-4a0ea44c94f6.png">

``babel-jest``がないようなので、インストールする。

```
$ npm i --save-dev babel-jest
```

``jest``コマンドを叩いて、テストが走るかを確認する。

<img width="707" alt="スクリーンショット 2020-07-15 12.21.45.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/402245/40088b21-607b-caad-d113-29d4f98a3e92.png">

無事に走っているようなので、試しに``__tests__``フォルダを作成して、JavaScriptで簡単なテストを試してみる。

```js:__tests__/index.test.js
it(' 1+2=3 である', () => {
    expect(1+2).toBe(3)
})
```

<img width="413" alt="スクリーンショット 2020-07-15 12.25.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/402245/8beff859-4211-d805-6fce-41ec98cd70af.png">

無事にテストが通ったので、``package.json``に設定した``"transform"``がうまく機能するか確かめるため、TypeScriptで簡単なテストを試してみる。

```tsx:sum.tsx
export const sum = (x: number, y: number) => x + y
```

```tsx:__test__/sum.test.tsx
import { sum } from '../sum'

it(' 1+2=3 である', () => {
    expect(sum(1, 2)).toBe(3)
})
```
<img width="697" alt="スクリーンショット 2020-07-15 12.52.19.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/402245/80f9583e-7ab2-feec-7883-2788b88ed826.png">

いろいろと警告が出ているが、テストは通っているので一旦無視しておく。

``.jest``フォルダが作られているので、gitに反映させないように``.gitignore``に追記する。

```text:.gitignore
# jest
.jest
```

目的である、``HomeTitle``コンポーネントのテストをするため、公式通りに必要なパッケージをインストールし、テストを書いて試してみる。

```
$ npm i --save-dev react-addons-test-utils
```

```tsx:__test__/HomeTitle.test.tsx
import React from 'react'
import renderer from 'react-test-renderer'

import HomeTitle from '../elements/HomeTitle'

it('renders correctly with defaults', () => {
    const title = renderer
      .create(<HomeTitle />)
      .toJSON()
    expect(title).toMatchSnapshot()
})
```

``jest``コマンドを実行していろいろ試すと、下記の3パターンのエラーが起こった。

<img width="711" alt="スクリーンショット 2020-07-15 12.37.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/402245/7077a0ff-cb1b-f0dc-0265-2bf1bee2b019.png">
<img width="703" alt="スクリーンショット 2020-07-15 14.06.20.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/402245/b0d72cb0-0033-88f1-ac0b-38280ca51fb7.png">
<img width="709" alt="スクリーンショット 2020-07-15 14.06.57.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/402245/0b435464-456a-efaa-dd0a-5865f4e64c44.png">

私の場合は、``react-test-renderer``をインストールしていなかった(``@types/react-test-renderer``をインストールしていた)、``jest.config.js``の設定をしていなかった、ということが問題だった。
[参考]
https://github.com/kulshekhar/ts-jest/issues/937
https://github.com/storybookjs/storybook/issues/1409

エラーを解消するため、下記のインストールと設定を行う。

```
$ npm i react-test-renderer
```

```jest.config.js
module.exports = {
    preset: 'react-native',
    transform: {
      '^.+\\.tsx?$': 'babel-jest',
    },
}
```

再び``jest``コマンドを実行すると、テストが通り、``__snapshots__``フォルダが作成された。

```__tests__/__snapshots__/HomeTitle.test.tsx.snap
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`renders correctly with defaults 1`] = `
<Text
  style={
    Object {
      "color": "#fff",
      "fontSize": 52.5,
    }
  }
>
  多面！フラッシュ暗算
</Text>
`;
```

``HomeTitle.tsx``の「！」を半角に変えてから再びテストを実行すると、差分が検知された。

<img width="713" alt="スクリーンショット 2020-07-15 14.09.53.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/402245/fbbf77be-b255-76f7-518f-1c7e5a19c1b2.png">

# Enzymeの導入

せっかくだからEnzymeも導入してみた。
[参考]
https://enzymejs.github.io/enzyme/docs/guides/react-native.html
https://medium.com/@hdsenevi/unit-testing-in-react-native-with-jest-and-enzyme-40cd7dabb6f1

必要なパッケージをインストールしていく。

```
$ npm i --save-dev enzyme enzyme-adapter-react-16 react-dom
```

TypeScriptで書いている場合はこちらも必要なようです。

```
$ npm i --save-dev @types/enzyme @types/enzyme-adapter-react-16 @types/react-dom
```

同じく``HomeTitle``コンポーネントのテストをするため、試しにデバッグを実行してみる。

```__tests__/HomeTitle-enzyme.test.tsx
import React from 'react'
import { configure, shallow } from 'enzyme'
import Adapter from 'enzyme-adapter-react-16';

import HomeTitle from '../elements/HomeTitle'

configure({ adapter: new Adapter() });

it('Should render HomeTitle', () => {
    const component = shallow(<HomeTitle />)
    console.log(component.debug())
})
```
<img width="676" alt="スクリーンショット 2020-07-15 14.26.39.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/402245/7cc8bca3-4076-6deb-75f7-c55b0dd469d3.png">

うまくEnzymeが導入できていることがわかる。
今回は、``<Text>``コンポーネントが正しくレンダリングされているかをテストするため、テスト用に``data-test="homeTitle"``を付加し、テストを実行する。

```HomeTitle.tsx
...
const HomeTitle: React.FC = () => {
    return <Text style={styles.title} data-test="homeTitle">多面！フラッシュ暗算</Text>;
};
...
```

```__tests__/HomeTitle-enzyme.test.tsx

...
it('Should render HomeTitle', () => {
    const component = shallow(<HomeTitle />)
    const wrapper = component.find(`[data-test='homeTitle']`)
    expect(wrapper.length).toBe(1)
})
```
<img width="712" alt="スクリーンショット 2020-07-15 14.31.57.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/402245/095c282e-5652-26dd-a39d-aa2dbbe6c227.png">

``HomeTitle-enzyme.test.tsx``は通っているが、``data-test="homeTitle"``を付加したため、先ほどJestで書いたスナップショットテストが通っていない。

テスト実行時にスナップショットを更新するために、``-u``オプションをつけて実行する。

```
jest -u
```
<img width="461" alt="スクリーンショット 2020-07-15 14.33.11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/402245/a487fae6-8646-4a1a-e31b-f19ec9463bd6.png">

無事にテストが通る。

# Huskyを導入

せっかくテストを書いたので、``git push``時にテストが走るようにしていく。
[参考]
https://github.com/typicode/husky

まずは、``npm run test``でJestを実行するscriptを``package.json``に追記する。

```package.json
"scripts": {
    "start": "expo start",
    "android": "expo start --android",
    "ios": "expo start --ios",
    "web": "expo start --web",
    "eject": "expo eject",
    "prettier": "npx prettier --write '**/*.{ts,tsx}'",
    "test": "jest" // 追加
  },
```

``npm run test``を実行して確認する。

<img width="578" alt="スクリーンショット 2020-07-15 15.12.30.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/402245/f542b0c0-51af-8b68-a83e-b0867561adc4.png">

Huskyをインストールする。

```
$ npm i --save-dev husky
```

``package.json``にHuskyの設定を追記する。

```package.json
"husky": {
    "hooks": {
      "pre-push": "CI=true npm run test"
    }
 },
```

``git push``時にテストが走るかどうか確認する。
<img width="704" alt="スクリーンショット 2020-07-15 15.17.43.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/402245/4be2b384-c16d-b2b5-0b17-fad76d98c99a.png">

# まとめ

とりあえず Jest, Enzyme, Husky の導入はできた。
正直、スナップショットテストもEnzymeの仕様も深くは知らないので、これを機に深めていきたい。
Jest, Enzymeは下のyoutubeの動画の教材がとても分かりやすかった。
[React Redux Unit & Integration Testing with Jest and Enzyme](https://www.youtube.com/playlist?list=PL-Db3tEF6pB8Am-IhCRgyGSxTalkDpUV_)