apeescape2.com
  • メイン
  • その他
  • 計画と予測
  • 製品の担当者とチーム
  • Kpiと分析
Webフロントエンド

React統合テストでコードの保守性を向上させる

統合テストは、テストのコストと価値の間のスイートスポットです。コンポーネントユニットテストの代わりに、またはコンポーネントユニットテストに加えて、react-testing-libraryを使用してReactアプリの統合テストを作成すると、開発速度を損なうことなくコードの保守性を向上させることができます。

先に進む前に有利なスタートを切りたい場合は、Reactアプリ統合テストにreact-testing-libraryを使用する方法の例をご覧ください。 ここに 。

統合テストに投資する理由

「統合テストは、信頼性と速度/費用の間のトレードオフに大きなバランスを取ります。これが、あなたの努力の大部分(すべてではありませんが、気に留めてください)をそこで過ごすことが賢明な理由です。
– Kent C. Dodds in テストを書く。あまり多くはありません。主に統合。



Reactコンポーネントの単体テストを作成するのが一般的な方法であり、Reactの「酵素」をテストするために一般的なライブラリを使用することがよくあります。具体的には、その「浅い」方法です。このアプローチにより、アプリの他の部分から分離してコンポーネントをテストできます。ただし、Reactアプリの作成はコンポーネントの作成がすべてであるため、単体テストだけではアプリにバグがないことを保証できません。

たとえば、コンポーネントの受け入れられた小道具を変更し、関連する単体テストを更新すると、すべてのテストに合格する可能性がありますが、別のコンポーネントがそれに応じて更新されなかった場合、アプリはまだ壊れている可能性があります。

統合テストは、コンポーネントの構成が目的のUXをもたらすことを保証するため、Reactアプリに変更を加える際の安心感を維持するのに役立ちます。

Reactアプリ統合テストの要件

ここにいくつかのものがあります React開発者 欲しいです 統合テストを作成するときに行うこと:

  • ユーザーの観点からアプリケーションのユースケースをテストします。ユーザーはWebページの情報にアクセスし、使用可能なコントロールを操作します。
  • 模擬API呼び出しは、テストの合格/不合格のAPIの可用性と状態に依存しません。
  • 模擬ブラウザAPI(ローカルストレージなど)は、テスト環境に存在しないためです。
  • React DOM状態(ブラウザーDOMまたはネイティブモバイル環境)でアサートします。

さて、いくつかのことのために私たちはしようとすべきです 避ける Reactアプリ統合テストを作成する場合:

  • 実装の詳細をテストします。実装の変更は、実際にバグが発生した場合にのみテストを中断する必要があります。
  • 嘲笑しすぎる。アプリのすべての部分がどのように連携しているかをテストしたいと思います。
  • 浅いレンダリング。アプリ内のすべてのコンポーネントの構成を最小のコンポーネントまでテストしたいと思います。

React-testing-libraryを選ぶ理由

前述の要件により react-testing-library その主な指針となる原則は、Reactコンポーネントを実際の人間がどのように使用するかに似た方法でテストできるようにすることであるため、優れた選択肢です。

ライブラリとそのオプション コンパニオンライブラリ 、DOMと相互作用し、その状態をアサートするテストを作成できます。

サンプルアプリのセットアップ

サンプル統合テストを作成するアプリは、単純なシナリオを実装します。

  • ユーザーはGitHubユーザー名を入力します。
  • アプリは、入力されたユーザー名に関連付けられた公開リポジトリのリストを表示します。

上記の機能がどのように実装されるかは、統合テストの観点からは無関係である必要があります。ただし、実際のアプリケーションに近づけるために、アプリは一般的なReactパターンに従います。したがって、アプリは次のようになります。

  • シングルページアプリ(SPA)です。
  • APIリクエストを作成します。
  • グローバルな状態管理を行っています。
  • 国際化をサポートします。
  • Reactコンポーネントライブラリを利用します。

アプリ実装のソースコードは次のとおりです。 ここに 。

市場戦略フレームワークに行く

統合テストの作成

依存関係のインストール

糸付き:

yarn add --dev jest @testing-library/react @testing-library/user-event jest-dom nock

またはnpmで:

npm i -D jest @testing-library/react @testing-library/user-event jest-dom nock

統合テストスイートファイルの作成

viewGitHubRepositoriesByUsername.spec.jsという名前のファイルを作成します./test内のファイルアプリケーションのフォルダ。ジェストは自動的にそれを拾います。

テストファイルへの依存関係のインポート

import React from 'react'; // so that we can use JSX syntax import { render, cleanup, waitForElement } from '@testing-library/react'; // testing helpers import userEvent from '@testing-library/user-event' // testing helpers for imitating user events import 'jest-dom/extend-expect'; // to extend Jest's expect with DOM assertions import nock from 'nock'; // to mock github API import { FAKE_USERNAME_WITH_REPOS, FAKE_USERNAME_WITHOUT_REPOS, FAKE_BAD_USERNAME, REPOS_LIST } from './fixtures/github'; // test data to use in a mock API import './helpers/initTestLocalization'; // to configure i18n for tests import App from '../App'; // the app that we are going to test

テストスイートの設定

describe('view GitHub repositories by username', () => { beforeAll(() => { nock('https://api.github.com') .persist() .get(`/users/${FAKE_USERNAME_WITH_REPOS}/repos`) .query(true) .reply(200, REPOS_LIST); }); afterEach(cleanup); describe('when GitHub user has public repositories', () => { it('user can view the list of public repositories for entered GitHub username', async () => { // arrange // act // assert }); }); describe('when GitHub user has no public repositories', () => { it('user is presented with a message that there are no public repositories for entered GitHub username', async () => { // arrange // act // assert }); }); describe('when GitHub user does not exist', () => { it('user is presented with an error message', async () => { // arrange // act // assert }); }); });

ノート:

  • すべてのテストの前に、GitHub APIをモックして、特定のユーザー名で呼び出されたときにリポジトリのリストを返します。
  • 各テストの後で、テストReact DOMをクリーンアップして、各テストがクリーンな場所から開始されるようにします。
  • describeブロックは、統合テストのユースケースとフローのバリエーションを指定します。
  • テストしているフローのバリエーションは次のとおりです。
    • ユーザーは、パブリックGitHubリポジトリに関連付けられている有効なユーザー名を入力します。
    • ユーザーは、パブリックGitHubリポジトリが関連付けられていない有効なユーザー名を入力します。
    • ユーザーがGitHubに存在しないユーザー名を入力しました。
  • itブロックは、テストしているユースケースに非同期ステップがあるため、非同期コールバックを使用します。

最初のフローテストの作成

まず、アプリをレンダリングする必要があります。

const { getByText, getByPlaceholderText, queryByText } = render();

render @testing-library/reactからインポートされたメソッドモジュールは、テストReact DOMでアプリをレンダリングし、レンダリングされたアプリコンテナーにバインドされたDOMクエリを返します。これらのクエリは、対話およびアサートするDOM要素を見つけるために使用されます。

ここで、テスト対象のフローの最初のステップとして、ユーザーにユーザー名フィールドが表示され、ユーザー名文字列が入力されます。

userEvent.type(getByPlaceholderText('userSelection.usernamePlaceholder'), FAKE_USERNAME_WITH_REPOS);

userEventインポートされた@testing-library/user-eventからのヘルパーモジュールにはtypeがありますユーザーがテキストフィールドにテキストを入力するときのユーザーの動作を模倣するメソッド。入力を受け入れるDOM要素とユーザーが入力する文字列の2つのパラメーターを受け入れます。

ユーザーは通常、それらに関連付けられたテキストによってDOM要素を見つけます。入力の場合は、ラベルテキストまたはプレースホルダーテキストのいずれかです。 getByPlaceholderText renderから以前に返されたクエリメソッドプレースホルダーテキストでDOM要素を見つけることができます。

テキスト自体は変更される可能性が高いため、実際のローカリゼーション値に依存せず、代わりにローカリゼーションアイテムキーを値として返すようにローカリゼーションモジュールを構成することをお勧めします。

たとえば、「en-US」ローカリゼーションが通常Enter GitHub usernameを返す場合userSelection.usernamePlaceholderの値としてキー、テストでは、userSelection.usernamePlaceholderを返すようにします。

nodejs静的サイトジェネレーター

ユーザーがフィールドにテキストを入力すると、テキストフィールドの値が更新されていることがわかります。

expect(getByPlaceholderText('userSelection.usernamePlaceholder')).toHaveAttribute('value', FAKE_USERNAME_WITH_REPOS);

次にフローで、ユーザーは送信ボタンをクリックし、リポジトリのリストが表示されることを期待します。

userEvent.click(getByText('userSelection.submitButtonText').closest('button')); getByText('repositories.header');

userEvent.clickメソッドは、ユーザーがDOM要素をクリックするのを模倣しますが、getByTextクエリは、含まれているテキストによってDOM要素を検索します。 closest修飾子は、正しい種類の要素を選択することを保証します。

注意: 統合テストでは、多くの場合、ステップは両方に役立ちますactおよびassert役割。たとえば、ユーザーがボタンをクリックすることでボタンをクリックできることを表明します。

前の手順で、ユーザーにアプリのリポジトリリストセクションが表示されることを確認しました。ここで、GitHubからリポジトリのリストをフェッチするのに時間がかかる可能性があるため、ユーザーにはフェッチが進行中であることが示されていることを確認する必要があります。また、リポジトリリストがまだフェッチされている間、入力されたユーザー名に関連付けられたリポジトリがないことをアプリがユーザーに通知しないようにする必要があります。

getByText('repositories.loadingText'); expect(queryByText('repositories.empty')).toBeNull();

getByに注意してくださいクエリプレフィックスは、DOM要素が見つかり、queryByであることを表明するために使用されます。クエリプレフィックスは、反対のアサーションに役立ちます。また、queryBy要素が見つからない場合、エラーを返しません。

次に、最終的に、アプリがリポジトリのフェッチを終了し、それらをユーザーに表示することを確認する必要があります。

await waitForElement(() => REPOS_LIST.reduce((elementsToWaitFor, repository) => { elementsToWaitFor.push(getByText(repository.name)); elementsToWaitFor.push(getByText(repository.description)); return elementsToWaitFor; }, []));

waitForElement非同期メソッドは、メソッドパラメーターとして提供されたアサーションをtrueにするDOM更新を待機するために使用されます。この場合、モックされたGitHubAPIによって返されるすべてのリポジトリの名前と説明がアプリに表示されることを表明します。

最後に、アプリはリポジトリがフェッチされていることを示すインジケーターを表示しなくなり、エラーメッセージも表示されなくなります。

expect(queryByText('repositories.loadingText')).toBeNull(); expect(queryByText('repositories.error')).toBeNull();

結果のReact統合テストは次のようになります。

it('user can view the list of public repositories for entered GitHub username', async () => { const { getByText, getByPlaceholderText, queryByText } = render(); userEvent.type(getByPlaceholderText('userSelection.usernamePlaceholder'), FAKE_USERNAME_WITH_REPOS); expect(getByPlaceholderText('userSelection.usernamePlaceholder')).toHaveAttribute('value', FAKE_USERNAME_WITH_REPOS); userEvent.click(getByText('userSelection.submitButtonText').closest('button')); getByText('repositories.header'); getByText('repositories.loadingText'); expect(queryByText('repositories.empty')).toBeNull(); await waitForElement(() => REPOS_LIST.reduce((elementsToWaitFor, repository) => { elementsToWaitFor.push(getByText(repository.name)); elementsToWaitFor.push(getByText(repository.description)); return elementsToWaitFor; }, [])); expect(queryByText('repositories.loadingText')).toBeNull(); expect(queryByText('repositories.error')).toBeNull(); });

代替フローテスト

ユーザーがパブリックリポジトリが関連付けられていないGitHubユーザー名を入力すると、アプリは適切なメッセージを表示します。

describe('when GitHub user has no public repositories', () => { it('user is presented with a message that there are no public repositories for entered GitHub username', async () => { const { getByText, getByPlaceholderText, queryByText } = render(); userEvent.type(getByPlaceholderText('userSelection.usernamePlaceholder'), FAKE_USERNAME_WITHOUT_REPOS); expect(getByPlaceholderText('userSelection.usernamePlaceholder')).toHaveAttribute('value', FAKE_USERNAME_WITHOUT_REPOS); userEvent.click(getByText('userSelection.submitButtonText').closest('button')); getByText('repositories.header'); getByText('repositories.loadingText'); expect(queryByText('repositories.empty')).toBeNull(); await waitForElement(() => getByText('repositories.empty')); expect(queryByText('repositories.error')).toBeNull(); }); });

ユーザーが存在しないGitHubユーザー名を入力すると、アプリはエラーメッセージを表示します。

describe('when GitHub user does not exist', () => { it('user is presented with an error message', async () => { const { getByText, getByPlaceholderText, queryByText } = render(); userEvent.type(getByPlaceholderText('userSelection.usernamePlaceholder'), FAKE_BAD_USERNAME); expect(getByPlaceholderText('userSelection.usernamePlaceholder')).toHaveAttribute('value', FAKE_BAD_USERNAME); userEvent.click(getByText('userSelection.submitButtonText').closest('button')); getByText('repositories.header'); getByText('repositories.loadingText'); expect(queryByText('repositories.empty')).toBeNull(); await waitForElement(() => getByText('repositories.error')); expect(queryByText('repositories.empty')).toBeNull(); }); });

React統合テストがロックする理由

統合テストは、Reactアプリケーションに最適な場所です。これらのテストは、バグを見つけて使用するのに役立ちます TDDアプローチ 同時に、実装が変更されたときにメンテナンスを必要としません。

この記事で紹介するReact-testing-libraryは、ユーザーと同じようにアプリを操作し、ユーザーの視点からアプリの状態と動作を検証できるため、React統合テストを作成するための優れたツールです。

ここで提供されている例が、新規および既存のReactプロジェクトの統合テストの作成を開始するのに役立つことを願っています。アプリの実装を含む完全なサンプルコードは、私の GitHub 。

基本を理解する

Reactテストとは何ですか?

Reactライブラリで記述されたユーザーインターフェイスの自動テスト。

ソフトウェアテストにおける統合テストとは何ですか?

統合テストは、システムの個別のコンポーネントが連携してユーザーの目標を達成していることを確認することを目的とした自動テストの変形です。

単体テストと統合テストの違いは何ですか?

統合テストではコンポーネントが一緒に機能することを確認し、単体テストでは各コンポーネントが独自に機能することを確認しますが、これはコンポーネントの統合に問題がないことを保証するものではありません。

エンドツーエンドテストとは何ですか?

エンドツーエンドテストは、すべてのシステムが連携してユーザーの目標を達成していることを確認することを目的とした自動テストの変形です。

なぜReactを使うべきなのですか?

これは、ユーザーインターフェースを構築するための優れた宣言型のコンポーネントベースのライブラリです。

フィンテック業界の現状(インフォグラフィック付き)

投資家と資金調達

フィンテック業界の現状(インフォグラフィック付き)
著名なデザイン会議2020

著名なデザイン会議2020

Uiデザイン

人気の投稿
直帰率を下げるためにカスタム読み込みアニメーションを作成する方法
直帰率を下げるためにカスタム読み込みアニメーションを作成する方法
デジタル遊牧民の冒険:ハッカーパラダイスと一緒に旅行
デジタル遊牧民の冒険:ハッカーパラダイスと一緒に旅行
WebpackまたはBrowserify&Gulp:どちらが良いですか?
WebpackまたはBrowserify&Gulp:どちらが良いですか?
強力なリモートワーク文化を構築する方法:ChristySchumannへのインタビュー
強力なリモートワーク文化を構築する方法:ChristySchumannへのインタビュー
これらのダッシュボードデザインのインスピレーションでアナリティクスをアップグレードする
これらのダッシュボードデザインのインスピレーションでアナリティクスをアップグレードする
 
devRantの禅
devRantの禅
プロジェクト管理会議2020の完全なリスト
プロジェクト管理会議2020の完全なリスト
人工知能が金融の世界にどのように影響しているか
人工知能が金融の世界にどのように影響しているか
モバイルユーザビリティの基本ガイド
モバイルユーザビリティの基本ガイド
UXデザイントレンドレトロスペクティブ2019
UXデザイントレンドレトロスペクティブ2019
人気の投稿
  • nodejsエラー処理のベストプラクティス
  • 次のルールのうち、資本予算分析に正しいものはどれですか?
  • sとcの法人の違い
  • scorpとccorpllcの違い
  • phpはUnicodeをASCIIに変換します
  • クレジットカードの暗証番号を解読する方法
カテゴリー
分散チーム Kpiと分析 設計プロセス ライフスタイル プロセスとツール アジャイルタレント Uxデザイン リモートの台頭 トレンド データサイエンスとデータベース

© 2021 | 全著作権所有

apeescape2.com