16
18

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

React Nativeのテストの話

Last updated at Posted at 2019-02-28
1 / 48

自己紹介

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

image.png


Goal

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


#jestいいよ


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


jest とは

  • config いらずで
  • snapshotテストもできて
  • 平行実行可能で
  • 多くのAPIを提供している
    image.png

react-nativeならjestすぐに使えるよ

react-nativeのバージョンが0.38以上であれば、react-native init 時にjestがセットアップされています。

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

jestすぐに使えるよ

$ yarn test

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


no configとはいえ


なんだかんだ設定は必要な時もある


eslint 使っている場合

eslintエラーが出ているのでeslint-plugin-jestを追加してください。

$ yarn add --dev eslint eslint-plugin-jest
.eslintrc
{
  "plugins": ["jest"]
}

TypeScript

ts-jestのインストールがてっとり早い


jestの基本


methodは標準的なものを取り揃えています

image.png


describe(name, fn) と test(name, fn, timeout)

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

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 になってしまう。

// 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を複数のデータセットで回したい時に利用します。

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);
  });
});

最近書いたコード

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
変更されたファイルを検知して、関連するテストを実行してくれます。
個人的にメインな使い方です。高いDXTDDを実現してくれます。


一度実行すればjestがファイルを監視してくれる

image.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


おすすめな使い方

悦に浸ったり、githubにバッジをつけてみてください。jest-coverage-badges


API通信部分のテスト


nockでサーバーのモックを作る

$ yarn add nock

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


具体的には

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形式でスナップショットを生成し、以降のテストではそのスナップショットとコンポーネントを比較して変更があればエラーとして検知するものです。

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

Wantedlyより抜粋


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

16
18
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
18

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?