序章

スナップショットテストを使用すると、出力が引き続き期待どおりに動作することを確認できます。 これは、コードを再検討して時間の経過とともに更新を行うと、それらの変更によって何かが破損する可能性が高くなるため、便利です。

厳密なテスト駆動開発(TDD)とは異なり、標準的な方法では、最初に失敗したテストを記述してから、テストに合格するためのコードを記述しますが、スナップショットテストは別のアプローチを取ります。

Reactコンポーネントのスナップショットテストを作成するときは、最初にコードを動作状態にする必要があります。 次に、特定のデータを指定して、期待される出力のスナップショットを生成します。 スナップショットテストは、コンポーネントと一緒にコミットされます。 テストフレームワークであるJestは、スナップショットをテスト用にレンダリングされた出力と比較します。

テストが失敗した場合、それは2つのことを意味する可能性があります。 テスト結果が予期しないものである場合は、コンポーネントの問題に対処する必要がある場合があります。 テスト結果が予想される場合は、新しい出力をサポートするためにスナップショットテストを更新する必要があることを意味する場合があります。

このチュートリアルでは、スナップショットテストと、それらを使用してユーザーインターフェイス(UI)が予期せず変更されないようにする方法について説明します。

前提条件

このチュートリアルを完了するには、次のものが必要です。

このチュートリアルでは、 Visual Studio Code をコードエディターとして使用し、統合端末を実行するのに便利です。 ただし、これを選択したエディターとターミナルに置き換えることができます。

このチュートリアルは、ノードv14.7.0、npm v6.14.7、react v16.13.1、およびjestv24.9.0で検証されました。

ステップ1—テストするReactコンポーネントを作成する

まず、何かをテストするために、 Create ReactAppを使用してReactアプリを作成する必要があります。 このチュートリアルでは、プロジェクトはreact-snapshot-testsと呼ばれます。

ターミナルを開き、次のコマンドを実行します。

  1. npx create-react-app@3.4.1 react-snapshot-tests

次に、新しく作成したアプリディレクトリに移動します。

  1. cd react-snapshot-tests

次に、アプリを起動します。

  1. npm start

この時点で、Reactアプリが実行され、Webブラウザーで表示できるようになります。 次に、テストできるコンポーネントを作成する必要があります。

このチュートリアルでは、作成するコンポーネントが、受け取ったitems小道具をレンダリングします。

ターミナルで、srcの下にcomponentsサブディレクトリを作成します。

  1. mkdir src/components

次に、Items.jsコンポーネントを作成します。

  1. nano src/components/Items.js

次のコードをItems.jsに追加します。

src / components / Items.js
import React from 'react';
import PropTypes from 'prop-types';

/**
 * Render a list of items
 *
 * @param {Object} props - List of items
 */
function Items(props) {
  const { items = [] } = props;

  // A single item in the list, render a span.
  if (items.length === 1) {
    return <span>{items[0]}</span>;
  }

  // Multiple items on the list, render a list.
  if (items.length > 1) {
    return (
      <ul>
        {items.map(item => <li key={item}>{item}</li>)}
      </ul>
    );
  }

  // No items on the list, render an empty message.
  return <span>No items in list</span>;
}

Items.propTypes = {
  items: PropTypes.array,
};

Items.defaultProps = {
  items: []
};

export default Items;

このコードは、量に基づいてitemsプロップをレンダリングします。

  • 複数のアイテムがある場合、アイテムは順序付けられていないリスト(<ul>)に表示されます。
  • アイテムが1つしかない場合は、<span>要素で表示されます。
  • アイテムがない場合は、エラーメッセージが表示されます。

最後に、App.jsを更新して、コンポーネントをレンダリングします。

  1. nano src/App.js

App.jsの内容を次のように置き換えます。

src / App.js
import React, { Component } from 'react';
import Items from './components/Items';

class App extends Component {
  render() {
    const items = [
      'Shark',
      'Dolphin',
      'Octopus'
    ];
    return (
      <Items items={items} />
    );
  }
}

export default App;

ブラウザでアプリにアクセスすると、App.jsで設定した値のリストが表示された画面が表示されます。

Output
* Shark * Dolphin * Octopus

itemsが複数あったため、順不同で表示されます。

次に、スナップショットテストを追加します。

ステップ2—スナップショットテストの作成

開始するには、CreateReactAppによって生成されたApp.test.jsファイルを削除します。

  1. rm src/App.test.js

このチュートリアルでは必要ありません。

次に、 react-test-renderer をインストールします。これは、DOMを必要とせずにReactコンポーネントをJavaScriptオブジェクトとしてレンダリングできるようにするライブラリです。

  1. npm install react-test-renderer@16.13.1

最初のテストを追加しましょう。 開始するには、Items.test.jsファイルを作成します。

  1. nano src/components/Items.test.js

Itemsコンポーネントを、小道具として渡されるアイテムなしでレンダリングするテストを作成します。

src / components / Items.test.js
import React from 'react';
import renderer from 'react-test-renderer';

import Items from './Items';

it('renders correctly when there are no items', () => {
  const tree = renderer.create(<Items />).toJSON();
  expect(tree).toMatchSnapshot();
});

次に、テストを実行しましょう。 Create React Appは、テストを設定するためのすべての初期化を処理しました。

  1. npm test

"renders correctly when there are no items"の合格テストを取得する必要があります。

A passing test!

スナップショットテストを初めて実行するときは、__snapshots__ディレクトリ内に新しいスナップショットファイルが作成されていることに注意してください。 テストファイルの名前はItems.test.jsであるため、スナップショットファイルの名前はItems.test.js.snapです。

Items.tests.js.snapの内容は次のようになります。

src / components / __ snapshots __ / Items.test.js.snap
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`renders correctly when there are no items 1`] = `
<span>
  No items in list
</span>
`;

このスナップショットは、コンポーネントの正確な出力と一致します。

Jestはpretty-formatを使用して、スナップショットファイルを人間が読める形式にします。

これで、1つのアイテムがあり、複数のアイテムがある他の2つのシナリオのテストを作成できます。

Items.tests.jsを開きます:

  1. nano src/components/Items.test.js

次のコード行を追加します。

src / components / Items.test.js
// ...

it('renders correctly when there is a single item', () => {
  const items = ['one'];
  const tree = renderer.create(<Items items={items} />).toJSON();
  expect(tree).toMatchSnapshot();
});

it('renders correctly when there are multiple items', () => {
  const items = ['one', 'two', 'three'];
  const tree = renderer.create(<Items items={items} />).toJSON();
  expect(tree).toMatchSnapshot();
});

この時点で、3つのテストが作成されています。1つはアイテムなし、1つは単一アイテム、もう1つは複数アイテムです。

テストを再実行します。

  1. npm test

3つのテストすべてに合格するはずです。これで、__snapshots__ディレクトリに3つのスナップショットが作成されます。

次に、スナップショットテストを更新して、失敗したテストに対処します。

ステップ3—スナップショットテストの更新

スナップショットテストが必要な理由をよりよく理解するために、Itemsコンポーネントに変更を加えて、テストを再実行します。 これは、開発中のプロジェクトに変更が加えられたときに何が起こるかをシミュレーションしたものです。

Items.jsを開きます:

  1. nano src/components/Items.js

spanおよびli要素にクラス名を追加します。

src / components / Items.js
...
/**
 * Render a list of items
 *
 * @param {Object} props - List of items
 */
function Items(props) {
  const { items = [] } = props;

  // A single item in the list, render a span.
  if (items.length === 1) {
    return <span className="item-message">{items[0]}</span>;
  }

  // Multiple items on the list, render a list.
  if (items.length > 1) {
    return (
      <ul>
        {items.map(item => <li key={item} className="item-message">{item}</li>)}
      </ul>
    );
  }

  // No items on the list, render an empty message.
  return <span className="empty-message">No items in list</span>;
}

Items.propTypes = {
  items: PropTypes.array,
};

Items.defaultProps = {
  items: [],
};

export default Items;

テストを再実行しましょう:

  1. npm test

失敗したテスト結果が観察されます。

Failing tests

Jestは、既存のスナップショットを、更新された変更を使用してレンダリングされたコンポーネントと照合し、コンポーネントにいくつかの追加があったために失敗しました。 次に、スナップショットテストに導入された変更の差分が表示されます。

変更が予期されていない場合は、変更がデプロイされる前にエラーをキャッチし、エラーに対処できるようになりました。 変更が予想される場合は、スナップショットテストを更新して、正しく合格させる必要があります。

チュートリアルでは、これは予想される変更であると想定できます。 コンポーネントにクラス名を追加するつもりでした。 次に、スナップショットテストを更新する必要があります。

Jestがインタラクティブモードのときに、提供されているオプションを使用してuを押すと、スナップショットテストを更新できます。

Watch Mode Options

注:または、Jest をグローバルにインストールしている場合jest --updateSnapshotまたはjest -uを実行できます。

これにより、スナップショットが更新され、行った更新と一致するようになり、テストに合格します。

アイテムがない場合の以前のスナップショットテストは次のとおりです。

src / components / __ snapshots __ / Items.test.js.snap
//  ...

exports[`renders correctly when there are no items 1`] = `
<span>
  No items in list
</span>
`;

//  ...

そして、これがアイテムなしの新しく更新されたスナップショットテストです:

src / components / __ snapshots __ / Items.test.js.snap
//  ...

exports[`renders correctly when there are no items 1`] = `
<span
  className="empty-message"
>
  No items in list
</span>
`;

// ...

テストを更新した後、それらは合格します:

Passing tests

これで、テストに再び合格しました。 これが開発中のプロジェクトである場合は、意図した変更が将来の開発のために文書化されていることを知って、コードをデプロイできます。

結論

このチュートリアルでは、Reactコンポーネントのスナップショットテストを作成しました。 また、テストの失敗を経験するようにコンポーネントを変更しました。 最後に、スナップショットを更新してテストを修正しました。

これは、ライブプロジェクトの小さなシミュレーションでした。 テストの合格、不合格、および不合格への対処のこのサイクルは、開発ワークフローの一部になります。

スナップショットテストは、さまざまなテストツールの1つです。 したがって、アクションとレデューサーのテストを作成する必要がある場合があります。

スナップショットテストの基本を探求している間、より良いスナップショットテストを書くことについて学ぶことができることがたくさんあります。 スナップショットテストの詳細については、Jestのドキュメントのスナップショットのベストプラクティスをご覧ください。

Reactの詳細については、Reactトピックページで演習とプログラミングプロジェクトを確認してください。