Jestを使ってReactのコンポーネントをスナップショットテストしてみる

セットアップ いつも通りディレクトリ作成とnpmのセットアップをやります。 $ mkdir try-jest && cd try-jest $ npm init -y Reactインストール なにはともあ […]

広告ここから
広告ここまで

目次

    セットアップ

    いつも通りディレクトリ作成とnpmのセットアップをやります。

    $ mkdir try-jest && cd try-jest
    $ npm init -y
    

    Reactインストール

    なにはともあれReactまわりのコンポーネントを入れましょう。

    $ npm i -S react react-dom
    

    Babelセットアップ

    これもいつも通りですが、Babelでトランスパイルできるようにしておきます。

    パッケージインストール

    $ npm i -D babel-preset-es2015 babel-preset-react
    

    .babelrc

    {
      "presets": ["es2015", "react"]
    }
    

    テスト用コンポーネントを作る

    Jestを入れる前に、ひとまずコンポーネントを作ってみましょう。

    テストを動かしてみることが目標なので、簡単なコンポーネントを用意しましょう。
    今回はボタンのタイプとテキストを指定できるコンポーネントを作ってみました。

    ExampleBtn.js

    import React from 'react'
    
    export default class ExampleBtn extends React.Component {
      render () {
        return (
          <button
            type={this.props.type}
          >
            {this.props.children}
          </button>
        )
      }
    }
    

    ここからはこのコンポーネントをテストしていきます。

    Jestセットアップ

    それではいよいよJestをいれていきましょう。
    Jest本体とBabel用のパッケージ、それにテストでReactコンポーネントをレンダーするためのパッケージの3つをいれます。

    $ npm i -D babel-jest jest react-test-renderer
    

    /node_modules/jest/bin/jest.jsが実行ファイルになりますので、実行してみて以下のように表示されるか確認してみましょう。

    $ ./node_modules/jest/bin/jest.js  
    No tests found
    In /Users/EXAMPLE_USER/develop/try-jest
      5 files checked.
      testMatch: **/__tests__/**/*.js?(x),**/?(*.)(spec|test).js?(x) - 0 matches
      testPathIgnorePatterns: /node_modules/ - 5 matches
    Pattern: "" - 0 matches
    
    

    __tests__というディレクトリにテストファイルがない」と言われています。

    Jestのテストファイルはtestsディレクトリ配下に配置しますので、次は先ほど作ったコンポーネントのテストを書いてみましょう。

    Jestでテストする

    テストコードを書く

    まずはフォルダとファイルを用意します。

    $ mkdir -p __tests__/ExampleBtn
    $ touch __tests__/ExampleBtn/tests.js
    

    テストコードは以下のように書きます。

    renderer.create()でコンポーネントを作成します。

    __tests__/ExampleBtn/tests.js

    import React from 'react'
    import ExampleBtn from '../../ExampleBtn'
    import renderer from 'react-test-renderer'
    
    test('render test', () => {
      const component = renderer.create(
        <ExampleBtn type="text">Test</ExampleBtn>
      )
      let tree = component.toJSON()
      expect(tree).toMatchSnapshot()
    })
    

    テストを実行する

    テスト対象とテストファイルが揃ったのでテストを実行しましょう。

    $ ./node_modules/jest/bin/jest.js 
     PASS  __tests__/ExampleBtn/tests.js
      ✓ render test (12ms)
    
    Snapshot Summary
     › 1 snapshot written in 1 test suite.
    
    Test Suites: 1 passed, 1 total
    Tests:       1 passed, 1 total
    Snapshots:   1 added, 1 total
    Time:        0.771s, estimated 1s
    Ran all test suites.
    

    スナップショットテストとは?

    ここまでで実行したテストは「スナップショットテスト」とよばれるものです。

    Snapshot tests are a very useful tool whenever you want to make sure your UI does not change unexpectedly.
    A typical snapshot test case for a mobile app renders a UI component, takes a screenshot, then compares it to a reference image stored alongside the test. The test will fail if the two images do not match: either the change is unexpected, or the screenshot needs to be updated to the new version of the UI component.

    *translate by google
    スナップショットテストは、あなたのUIが予期せず変更されないようにするために非常に便利なツールです。
    モバイルアプリケーションの典型的なスナップショットテストケースは、UIコンポーネントをレンダリングし、スクリーンショットを撮ってから、テストと一緒に保存された参照イメージと比較します。 2つのイメージが一致しない場合、テストは失敗します。変更が予期しない場合、またはスクリーンショットを新しいバージョンのUIコンポーネントに更新する必要がある場合。

    via:https://facebook.github.io/jest/docs/snapshot-testing.html

    .toMatchSnapshot()を実行する度に、__snapshots_/test.js.snapにスナップショットが保存され、テスト実行時に比較が行われます。

    __tests__/ExampleBtn/__snapshots__/test.js.snap

    // Jest Snapshot v1, https://goo.gl/fbAQLP
    
    exports[`render test 1`] = `
    <button
      type="text"
    >
      Test
    </button>
    `;
    
    

    スナップショットテストをFailさせてみる

    せっかくなので、このテストを失敗させてみましょう。

    buttonタグのテキストを「Test | {指定した文字}」という表示になるように変更してみました。

    import React from 'react'
    
    export default class ExampleBtn extends React.Component {
      render () {
        return (
          <button
            type={this.props.type}
          >
            Hoge |
            {this.props.children}
          </button>
        )
      }
    }
    

    スナップショットに記録された出力内容と異なる出力になるので、これでテストがコケるはずです。

    $ ./node_modules/jest/bin/jest.js 
     FAIL  __tests__/ExampleBtn/test.js
      ● render test
    
        expect(value).toMatchSnapshot()
    
        Received value does not match stored snapshot 1.
    
        - Snapshot
        + Received
    
         <button
           type="text"
         >
        +  Hoge |
           Test
         </button>
    
          at Object.<anonymous> (__tests__/ExampleBtn/test.js:10:16)
          at process._tickCallback (internal/process/next_tick.js:103:7)
    
      ✕ render test (28ms)
    
    Snapshot Summary
     › 1 snapshot test failed in 1 test suite. Inspect your code changes or re-run with `-u` to update them.
    
    Test Suites: 1 failed, 1 total
    Tests:       1 failed, 1 total
    Snapshots:   1 failed, 1 total
    Time:        1.238s
    Ran all test suites.
    
    

    はい。見事にこけました。

    このテストを入れておくことで、「コンポーネントの出力内容がいつのまにか変わっている」という事故を防ぐことができそうですね。

    「コンポーネントの変更したいんだけど・・・」という場合

    コンポーネントの出力が前回テスト時のものと変わっているとコケるのがスナップショットテスト(という理解であっているはず)です。

    とするとコンポーネントの変更をやりたい場合はどうすれば良いのでしょうか。

    先ほどのFailしたテスト結果をよくみると、その方法が紹介されています。

    Inspect your code changes or re-run with -u to update them.
    (Google翻訳): コードの変更を調べるか、 -uで再実行してコードを更新してください。

    はい。-uオプションをつけて再実行すると良いみたいです。

    $ ./node_modules/jest/bin/jest.js  -u
     PASS  __tests__/ExampleBtn/test.js
      ✓ render test (17ms)
    
    Snapshot Summary
     › 1 snapshot updated in 1 test suite.
    
    Test Suites: 1 passed, 1 total
    Tests:       1 passed, 1 total
    Snapshots:   1 updated, 1 total
    Time:        0.944s, estimated 1s
    Ran all test suites.
    

    ということで再実行した結果、テストがPassしました。

    スナップショットを確認すると、先ほどのスナップショットが削除されて新しいものに置き換わっています。

    // Jest Snapshot v1, https://goo.gl/fbAQLP
    
    exports[`render test 1`] = `
    <button
      type="text"
    >
      Hoge |
      Test
    </button>
    `;
    
    

    所感

    Fail時のメッセージを読む限り、スナップショットテストはあくまで「予期せぬコンポーネント出力の変更を検知するためのテスト」という位置づけっぽいです。

    DOMテストの方法もドキュメントに記載されていますので、Jestでのテストではこのあたりを組み合わせて使うことになるのかなと思います。

    広告ここから
    広告ここまで
    Home
    Search
    Bookmark