React Nativeのテストの話

# 自己紹介
- 筑井 友啓 Tsukui Tomohiro
- ecbo株式会社(2018/4 ~ )
- 「ecbo cloak」というアプリをリリースしました
- twitter @two2q

---
![image.png](https://qiita-image-store.s3.amazonaws.com/0/40909/43677ccf-f975-e402-e435-02cae6eb46d7.png)

---
# Goal
明日にでもjest使ってくれたら嬉しいです

---
#jestいいよ

---
# かなりDXが高く、すぐにTDD出来る

---
# jest とは
- config いらずで
- snapshotテストもできて
- 平行実行可能で
- 多くのAPIを提供している
![image.png](https://qiita-image-store.s3.amazonaws.com/0/40909/bd973e94-66f0-b40b-fa07-98d21447eeef.png)

---
# react-nativeならjestすぐに使えるよ
react-nativeのバージョンが0.38以上であれば、`react-native init` 時にjestがセットアップされています。

```package.json
  "scripts": {
    "test": "jest"
  },
  "jest": {
    "preset": "react-native"
  }
```

---
# jestすぐに使えるよ

``` sh
$ yarn test
```

参考 https://jestjs.io/docs/ja/tutorial-react-native

---
# no configとはいえ
--- 
# なんだかんだ設定は必要な時もある
--- 
# eslint 使っている場合

eslintエラーが出ているので[eslint-plugin-jest](https://github.com/jest-community/eslint-plugin-jest)を追加してください。

```sh
$ yarn add --dev eslint eslint-plugin-jest
```

```.eslintrc
{
  "plugins": ["jest"]
}
```

---
# TypeScript
[ts-jest](https://github.com/kulshekhar/ts-jest)のインストールがてっとり早い



---
# jestの基本
---
# methodは標準的なものを取り揃えています
![image.png](https://qiita-image-store.s3.amazonaws.com/0/40909/0c9ce8d5-be2f-a6be-8a32-14e3c171acc9.png)

---
# describe(name, fn) と test(name, fn, timeout)
`describe`: いくつかのテストをまとめるブロックを作ります。
`test`: 実際にテストを書くブロックです。期待値との比較などを行います。`it(name, fn, timeout)` も同じです。

``` js
const myBeverage = {
  delicious: true,
  sour: false,
};

describe('my beverage', () => {
  test('is delicious', () => {
    expect(myBeverage.delicious).toBeTruthy();
  });

  test('is not sour', () => {
    expect(myBeverage.sour).toBeFalsy();
  });
});
```
---
# 非同期のテストについての注意
非同期関数をテストする際には、 `return` をしてあげないと絶対に `success` になってしまう。

``` js
// targetFn.js
const targetFn = async () => {  } // do something

// targetFn.spec.js
test('test async', () => {
  return targetFn().then((result) => {
   expect(result).toBe('expected')
  })
}
```
他にもasync () => { await ... } や (done) => { ... done() }
でもテスト可能です。

参考: https://jestjs.io/docs/en/tutorial-async

---
# おすすめなMethod
---
# describe.each(table)(name, fn, timeout)
同じtestを複数のデータセットで回したい時に利用します。

```js
describe.each`
  a    | b    | expected
  ${1} | ${1} | ${2}
  ${1} | ${2} | ${3}
  ${2} | ${1} | ${3}
`('$a + $b', ({a, b, expected}) => {
  test(`returns ${expected}`, () => {
    expect(a + b).toBe(expected);
  });

  test(`returned value not be greater than ${expected}`, () => {
    expect(a + b).not.toBeGreaterThan(expected);
  });

  test(`returned value not be less than ${expected}`, () => {
    expect(a + b).not.toBeLessThan(expected);
  });
});
```

---
# 最近書いたコード
```js
const baseDateString = '2019-02-26T18:30:00.000';
describe.each`
  language | expected      
  ${'en'}  | ${'02/26 6:30 PM'}
  ${'ja'}  | ${'02/26 18:30'}
  ${'zh-Hant'}  | ${'02/26 18:30'}
`('localizedDateTime with $language', ({ language, expected }) => {
  it(`should return ${expected}`, () => {
    i18n.changeMomentLocale(language);
    const result = dateTimeExpression.localizedDateTime(moment(baseDateString));
    expect(result).toEqual(expected);
  });
});
```

**見通しよく、データセットの追加も容易に！**
参照: https://jestjs.io/docs/en/api#describeeachtable-name-fn-timeout


---
# おすすめな実行オプション その1
---
# --watch
---
#--watch
**変更されたファイルを検知**して、関連するテストを実行してくれます。
**個人的にメインな使い方**です。**高いDX**で**TDD**を実現してくれます。

---
# 一度実行すればjestがファイルを監視してくれる
![image.png](https://qiita-image-store.s3.amazonaws.com/0/40909/39e3da7f-111e-be78-b1e2-4f9cf2946531.png)

---
# おすすめな使い方
1. 先にテスト対象のファイルの `[spec|test].js` を用意し、ある程度describeも書いておきます。
2. エディタのterminalで `$ jest --watch` します。
3. 全てのテストがerrorになります。
4. テストがパスするまで実装してください。

---
# 主な利用シーン
- ducks層の実装
- form validationの実装
- utils系の実装
- Component内の複雑なfunctionの実装

=> ピュアなjsでいける部分については基本的にjestで先に仕様をまとめてから実装します。

---
# おすすめな実行オプション その2
---
# --coverage
---
## jest --coverage
カバレッジを計測してくれます。ソース全体のカバレッジではなく、あくまでもjestで実行されたファイルのみを計測します。
カバレッジが重要ではないのですが、no configで実行出来るという点がかなり素敵かな、と思っています。

---
# 実行してみた
![image.png](https://qiita-image-store.s3.amazonaws.com/0/40909/af2dd415-38a5-efa5-2789-69a28cbca00e.png)

---
# おすすめな使い方
悦に浸ったり、githubにバッジをつけてみてください。[jest-coverage-badges](https://github.com/pamepeixinho/jest-coverage-badges)

---
# API通信部分のテスト
---
# nockでサーバーのモックを作る
```sh
$ yarn add nock
```
指定したリクエストを上書きしてモックデータを返却してくれます。一度レスポンスの型を決めてしまえばAPI通信部分のテストをサーバーなしで実行出来るようになります。

---
# 具体的には
``` js
import nock from 'nock';

nock('http://www.example.com')
  .get('/hogehoge')
  .reply(200, { foo: 'bar' })
```

こうする事で、`http://www.example.com`へのrequestが全てinterceptされるようになり、`/hogehoge`への`get`リクエストにはstatus200と共に `{foo: 'bar'}` が返却されるということを意味します。
なお、jestにも標準でmockが用意されているのですが、モジュールのモックになってしまうのでダミーデータを返却するクライアントのモックを用意する必要があります。その点nockは一手間減るという印象があります。

---
# snapshot テストについて
初回snapshotテスト実行時に`.snap`形式でスナップショットを生成し、以降のテストではそのスナップショットとコンポーネントを比較して変更があればエラーとして検知するものです。

```js
import renderer from "react-test-renderer";
import App from "../App";

// 省略

  it("renders the loading screen", async () => {
    const tree = renderer.create(<App />).toJSON();
    expect(tree).toMatchSnapshot();
  });

```
---
# コンポーネントの品質についてはstorybookの方が今の所効果あります

- そもそも変更が多いのでsnapshotの恩恵が少ない（影響範囲がわかるのはよかった）
- 実際に操作をさせないとバグかどうかの判断がつきにくい
- `@storybook/addon-knobs`を整備してあげてデザイナーさんに触ってもらった方が結局早い

---
# TODO E2Eテスト
---
# こんな感じで、色々jestのキャッチアップをしながらコードを書いています。
---
# なぜ書くのか

---
# ひどいデグレは防げるかもしれない
- 障害対応で疲弊する事が減るかもしれない
- 修正に対する恐怖が減る、下手なオンボーディングよりも安心する

---
# 過去の自分が書いた負債で後任が爆死しない
- specさえあれば負債の負債度が減る
- 恨みを買わなくて済むので平和になる
- ここクソだけどspecあるからひとまず安心する、とか言われる

---
# わざわざ端末触らなくても実装出来る
- 端末の起動よりもjestの方がはるかに早いので少し早く帰れる
- 端末をフリフリしてどっかに飛ばすリスクが減る
 
---
# 思い切ったリファクタも結構出来る
- もちろん程度にもよるが、ないよりははるかにマシ

---
# コードレビューのフィードバック対応も早い
- 指摘内容はそれに対応するspecを添えればレビュアーにとってもわかりやすい
- フィードバック修正後のテスト工数の削減

---
# 基本的にいい事しかないし、健全な働きが出来る

---
# テストは福利厚生と言ってもいいのではないだろうか？

---
# ひとまず福利厚生に書いてみた
![image.png](https://qiita-image-store.s3.amazonaws.com/0/40909/5a289072-e973-9d99-0c42-f89bd9e1f489.png)

Wantedlyより抜粋

---
# 一緒に世界中の人に使ってもらえるアプリを作りませんか？