LoginSignup
0
1

More than 3 years have passed since last update.

ReactコンポーネントをJestでスナップショットテストする

Last updated at Posted at 2021-02-05

はじめに

最近Reactを勉強し始めた初心者です。今まで、バックエンドのユニットテストなどは書いたことがあるのですが、フロントエンドのテスト手法に、スナップショットテストというものがあると知り、作成してみました。また、今回は create-react-app を利用しているので、最初から使える Jest をテストフレームワークとして利用します。

目次

1. 検証環境
2. スナップショットテストとは
3. コンポーネントのスナップショットテストを作成する
4. プログラムソース
5. 所感

1. 検証環境

  • OS: macOS Big Sur Version 11.1
  • Chip: Apple M1
  • typescript: 4.1.3
  • react: 17.0.1
  • jest: 26.6.3

2. スナップショットテストとは

スナップショットテストは、予期しないUIの変更を確認したいときに、有用なテストです。一般的には、UIコンポーネントをレンダリングし、そのスナップショットを取得して、以前、取得した参照用のスナップショットと比較します。2つのスナップショットが一致しない場合、テストが失敗するため、何らかの変更が起こったときに簡単に検知することができます。

また、スナップショットの生成物は、コードの変更と一緒にコミットし、コードレビューのプロセスの一部としてレビューするします。Jestは、以下のように、コードレビューで人間が読めるように、可読性の高い形でスナップショットが生成されます。

exports[`renders correctly 1`] = `
<a
  className="normal"
  href="http://www.facebook.com"
  onMouseEnter={[Function]}
  onMouseLeave={[Function]}
>
  Facebook
</a>
`;

テストが失敗した場合(UIに変更が起こった場合)は、スナップショットの内容をレビューし、バグとして修正するか、実装された変更が正しいと判断し、スナップショットを更新する必要があります。

なんだか、今まで実装したことのある、ユニットテストとは少し感う印象です。

3. コンポーネントのスナップショットテストを作成する

3.1 フォルダ構成の決定

create-react-appを利用していれば、最初からJestは利用できるはずなので、フォルダ構成の決定から実施します。create-react-appでは、以下のパターンにマッチすると、テストコードとして認識します。

  • __tests__フォルダ内にある、.js/tsx拡張子のファイル
  • .test.js/tsx拡張子のファイル
  • .spec.js/tsx拡張子のファイル

今回は、scr/__tests__を作成して.test.tsx拡張子でテストコードを作成したいと思います。フォルダ階層は以下です。

src
├── __tests__
│   └── components
│       └── molecules
│           └── Copyright.test.tsx
├── components
│   └── molecules
│       └── Copyright.tsx

3.2 テスト対象のコンポーネントの説明

フッターにコピーライトの文字を返すコンポーネントです。UIフレームワークには、material-uiを利用してます。

Copyright.tsx
import { FC } from 'react';
import Typography from '@material-ui/core/Typography';

type CopyrightProps = {
  authorName: string;
};

const Copyright: FC<CopyrightProps> = ({ authorName }) => (
  <Typography variant="body2" color="textSecondary" align="center">
    {'© '}
    {new Date().getFullYear()} {authorName}.
  </Typography>
);

export default Copyright;

3.3 react-test-rendererのインストール

create-react-appを利用すると、jestは使えるのですが、react-test-rendererが入ってないので、インストールします。私はTypescriptを利用しているので、@types/react-test-rendererts-jest もインストールします。

yarn add --dev react-test-renderer
yarn add --dev @types/react-test-renderer
yarn add --dev ts-jest

3.4 テストコードの作成

以下のようにテストコードを作成しました。

Copyright.test.tsx
import * as React from 'react';
import * as renderer from 'react-test-renderer';
import Copyright from '../../../components/molecules/Copyright';

it('renders correctly', () => {
  const tree = renderer.create(<Copyright authorName="test1" />).toJSON();
  expect(tree).toMatchSnapshot();
});

3.5 テストの実行

以下のコマンドで、テストを実行してみましょう。

$ yarn test
 PASS  src/__tests__/components/molecules/Copyright.test.tsx
  Copyright componet
    ✓ renders correctly (14 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   1 passed, 1 total
Time:        0.787 s, estimated 1 s
Ran all test suites related to changed files.

Watch Usage
 › Press a to run all tests.
 › Press f to run only failed tests.
 › Press q to quit watch mode.
 › Press p to filter by a filename regex pattern.
 › Press t to filter by a test name regex pattern.
 › Press Enter to trigger a test run.

無事テストが成功しました。テストを実行すると、テストファイルと同じ場所に、__snapshopts__/Copyright.test.tsx.snap というスナップショットが作成されました。内容は以下のようになっていました。

Copyright.test.tsx.snap
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Copyright componet renders correctly 1`] = `
<p
  className="MuiTypography-root MuiTypography-body2 MuiTypography-colorTextSecondary MuiTypography-alignCenter"
>
  © 
  2021

  test1
  .
</p>
`;

テストが正しく動くかを検証するために、テストコードのpropsを変更してみます。

Copyright.test.tsx
import * as React from 'react';
import * as renderer from 'react-test-renderer';
import Copyright from '../../../components/molecules/Copyright';

describe('Copyright componet', () => {
  it('renders correctly', () => {
    const tree = renderer.create(<Copyright authorName="test2" />).toJSON();
    expect(tree).toMatchSnapshot();
  });
});

テストを再度実行すると、以下の様になりました。

$ yarn test
FAIL  src/__tests__/components/molecules/Copyright.test.tsx
  Copyright componet
    ✕ renders correctly (15 ms)

  ● Copyright componet › renders correctly

    expect(received).toMatchSnapshot()

    Snapshot name: `Copyright componet renders correctly 1`

    - Snapshot  - 1
    + Received  + 1

    @@ -2,8 +2,8 @@
        className="MuiTypography-root MuiTypography-body2 MuiTypography-colorTextSecondary MuiTypography-alignCenter"
      >
        © 
        2021

    -   test1
    +   test2
        .
      </p>

       6 |   it('renders correctly', () => {
       7 |     const tree = renderer.create(<Copyright authorName="test2" />).toJSON();
    >  8 |     expect(tree).toMatchSnapshot();
         |                  ^
       9 |   });
      10 | });
      11 | 

      at Object.<anonymous> (src/__tests__/components/molecules/Copyright.test.tsx:8:18)

 › 1 snapshot failed.
Snapshot Summary
 › 1 snapshot failed from 1 test suite. Inspect your code changes or press `u` to update them.

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   1 failed, 1 total
Time:        1.232 s
Ran all test suites related to changed files.

Watch Usage
 › Press a to run all tests.
 › Press f to run only failed tests.
 › Press u to update failing snapshots.
 › Press i to update failing snapshots interactively.
 › Press q to quit watch mode.
 › Press p to filter by a filename regex pattern.
 › Press t to filter by a test name regex pattern.
 › Press Enter to trigger a test run.

ちゃんと差分が検知され、テストが失敗しました。この変更が、意図したものであれば、yarn test -u でスナップショットを更新し、バグであれば修正するという流れになります。

3.6 可変値に対応する

これで、テストは一旦完了ですが、一つだけ修正したい場所があります。このコンポーネントですが、new Date().getFullYear() を利用しており、翌年になると一切変更を指定なくてもテストが失敗します。このような、コンポーネント内にIDや日付などの可変値を含む際の対処を実施します。

以下のように、mockを利用し、Dateが固定値を返すように変更します。

Copyright.test.tsx
describe('<Copyright />', () => {
  it('should render correctly', () => {
    const mockDate = new Date(2021, 1, 1);
    const spy = jest
      .spyOn(global, 'Date')
      .mockImplementation(() => (mockDate as unknown) as string);

    const tree = renderer.create(<Copyright authorName="wkamuy" />).toJSON();
    expect(tree).toMatchSnapshot();

    spy.mockRestore();
  });
});

これで、テストは完了です。

4. プログラムソース

以下がプログラムソースです。

WKAMUY's GitHub Repository

以下が実際のWEBページです

WKAMUY.PAGES

5. 所感

今回はスナップショットテストを作成しました。かなり手軽に作成できるので、UIのテストの一つとして導入するのは結構有用かなと思います。DOMの操作などもできるようなので、必要な部分は実装してみたいなと思います。

0
1
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
0
1