ReactコンポーネントをJestでスナップショットテストする
はじめに
最近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を利用してます。
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-renderer
と ts-jest
もインストールします。
yarn add --dev react-test-renderer
yarn add --dev @types/react-test-renderer
yarn add --dev ts-jest
3.4 テストコードの作成
以下のようにテストコードを作成しました。
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
というスナップショットが作成されました。内容は以下のようになっていました。
// 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を変更してみます。
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が固定値を返すように変更します。
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. プログラムソース
以下がプログラムソースです。
以下が実際のWEBページです
5. 所感
今回はスナップショットテストを作成しました。かなり手軽に作成できるので、UIのテストの一つとして導入するのは結構有用かなと思います。DOMの操作などもできるようなので、必要な部分は実装してみたいなと思います。
Author And Source
この問題について(ReactコンポーネントをJestでスナップショットテストする), 我々は、より多くの情報をここで見つけました https://qiita.com/wkamuy/items/641144b8b49119ec9987著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .