材料の著者は、彼が長い間このようなことを考えていたと言い、その結果、先月中旬に、彼は彼に合ったテストライブラリの開発を開始することに決めました。 特に、酵素では、このライブラリの機能のほとんどが開発者を、プロジェクトに害を及ぼす可能性のあるテストを準備するための最良の方法ではないという事実を好まなかった。 その結果、彼はReact DOMをテストするためのシンプルだが自給自足のツールセットを手に入れました。
React-testing-libraryの概要
Reactコンポーネントのテストを作成するとします。 これらのテストを便利に維持する必要があります。 さらに、テストがコンポーネントの実装の詳細に依存しないことを確認し、実際の使用シナリオに近い条件でコンポーネントをテストする必要があります。 さらに、テストが長期にわたって機能し続けるようにします。つまり、コンポーネントのリファクタリング(つまり、機能ではなく実装の変更)がテストシステムに違反せず、あなたやチームを強制しないようにします。テストを絶えず書き換え、プロジェクトの作業を遅くします。
これらの目標を達成するために何を選択しますか? 私たちの場合、これらの質問に対する答えは、Reactコンポーネントをテストするために設計された最小限のソリューションである
react-testing-library
でした。
React-testing-libraryロゴ
このライブラリは、開発者に
react-dom
および
react-dom/test-utild
に基づいて構築されたシンプルなツールを提供します。さらに、ライブラリは、それを使用する人が問題なく作業でベストなテスト方法を使用できるように設計されています。
react-testing-library
は、 次の原則に基づいています 。テストプロセスがアプリケーションとの実際のセッションに似ているほど、アプリケーションが実稼働状態になると、期待どおりに動作すると確信できるようになります。
その結果、レンダリングされたReactコンポーネントのインスタンスを操作する代わりに、テストは実際のDOMノードと対話します。 ライブラリが提供するメカニズムは、プロジェクトユーザーと同じ方法でDOMを呼び出します。 要素はラベルのテキストで検索され(これはユーザーが行うことです)、リンクとボタンもテキストで検索されます(これはユーザーにとって典型的です)。 さらに、ここでは、「緊急出口」として、
data-testid
による要素の検索を適用することができます。 これは、この目的のために署名が無意味または非実用的である要素を操作できるようにする一般的な方法です。
考慮されるライブラリは、アプリケーションの可用性を高め、テストを実際の作業シナリオに近づけるのに役立ちます。
react-testing-library
は、 酵素の代替です。
enzyme
を使用
enzyme
、ここで検討するライブラリに固有の同じ原則に従うことができますが、この場合、
enzyme
によって提供される追加の資金のために、それらに従うことはより困難です(つまり、テストの実装の詳細に役立つすべて)。 詳細については、 こちらをご覧ください 。
さらに、
react-testing-library
react-dom
対象としてい
react-testing-library
、この小さな設定ファイルを使用することでReact Nativeにも適しています 。
このライブラリはテストやフレームワークを実行する手段ではないことをすぐに言及する価値があります。 さらに、テストのために特定のフレームワークに関連付けられていません(Jestをお勧めしますが、これは使用することを好むツールにすぎませんが、一般的に、ライブラリは任意のフレームワークで動作し、 CodeSandbox環境でも動作します!)
実用例
react-testing-library
を使用した実用的な例を示す次のコードを検討してください。
import React from 'react' import {render, Simulate, wait} from 'react-testing-library' // import 'react-testing-library/extend-expect' // Mock- __mocks__ import axiosMock from 'axios' import GreetingFetcher from '../greeting-fetcher' test('displays greeting when clicking Load Greeting', async () => { // Arrange axiosMock.get.mockImplementationOnce(({name}) => Promise.resolve({ data: {greeting: `Hello ${name}`} }) ) const { getByLabelText, getByText, getByTestId, container } = render(<GreetingFetcher />) // Act getByLabelText('name').value = 'Mary' Simulate.click(getByText('Load Greeting')) // mock- `get` // , await wait(() => getByTestId('greeting-text')) // Assert expect(axiosMock.get).toHaveBeenCalledTimes(1) expect(axiosMock.get).toHaveBeenCalledWith(url) // ! expect(getByTestId('greeting-text')).toHaveTextContent( 'Hello Mary' ) // DOM! expect(container.firstChild).toMatchSnapshot() })
この例から学ぶことができる最も重要なことは、テストが実際のユーザーアプリケーションでの動作に似ていることです。
このコードの分析を続けます。
GreetingFletcher
は、次のようなHTMLコードを出力できます。
<div> <label for="name-input">Name</label> <input id="name-input" /> <button>Load Greeting</button> <div data-testid="greeting-text"></div> </div>
これらの要素を使用する場合、次の一連のアクションが期待されます。名前を設定し、[
Load Greeting
の
Load Greeting
]ボタンをクリックします。これにより、サーバーへの要求が指定された名前が使用されるテキストを読み込みます。
テストでは、
<input />
フィールドを見つける必要があります。その結果、その
value
パラメーターを特定の値に設定できます。 常識的には、CSSセレクターで
id
プロパティを使用できることが示唆されています:
#name-input
。 しかし、これはユーザーが入力フィールドを見つけるために行うことですか? 確かにそうではありません! ユーザーは画面を見て、データを入力する署名
Name
を持つフィールドを見つけます。 したがって、これはまさにテスト
getByLabelText
です。 ラベルに基づいてコントロールを検出します。
多くの場合、
enzyme
に基づいて作成されたテストでは、
Load Greeting
などのボタンを検索するために、CSSセレクターが使用されるか、コンポーネントコンストラクターの
displayName
で検索が実行されます。 しかし、ユーザーがサーバーからテキストをダウンロードする場合、プログラムの実装の詳細については考えず、代わりに
Load Greetings
というラベルの付いたボタンを検索してクリックします。 これはまさに、
getByText
ヘルパー関数を使用してテストが行うことです。
これに加えて、
wait
構造もユーザーの動作をシミュレートします。 ここでは、画面にテキストが表示されるのを待って整理しました。 システムは必要な限り待機します。 テストでは、これにモックオブジェクトが使用されるため、テキストはほぼ瞬時に出力されます。 しかし、私たちのテストはそれがどれくらい時間がかかるかを気にしません。 テストで
setTimeout
などを使用する必要はありません。
「あいさつ文ノードが表示されるのを待ちます」というテストを伝えるだけです。 この場合、
data-testid
属性が使用されているという事実に注意してください。他のメカニズムを使用して要素を探すことが意味をなさない状況で使用されるのと同じ「緊急出口」。 このような場合、
data-testid
他の方法よりも間違いなく優れています。
APIの概要
当初、ライブラリは開発者に
queryByTestId
メソッドのみを提供してい
queryByTestId
。 ここでそれについて読んでください 。 ただし、上記の出版物への対応とこの素晴らしいパフォーマンスにより、追加のメソッドがライブラリに追加されました。
ライブラリとそのAPIの詳細は、 公式ドキュメントに記載されています 。 機能の一般的な概要を次に示します。
- Simulate - Simulate
react-dom/test-utils
から再エクスポートします。
- wait-テストで無期限の待機を整理できます。 通常、API 呼び出しまたはアニメーションにモックオブジェクトを使用する必要がありますが、すぐに解決されたプロミスを操作する場合でも、イベントループの次のティックを待つテストが必要です。
wait
メソッドはこれに最適です。 (これは非推奨となったflushPromises
APIの代替として提案したLukasz Gandeckiに感謝します)。
- render :ライブラリの本質全体がここに隠されています。 実際、これはかなり単純な関数です。
document.createElement
を使用してdiv
要素をdocument.createElement
、ReactDOM.render
を使用してこのdiv
データを出力します。
render
関数は、次のオブジェクトとヘルパー関数を返します。
- container :コンポーネントがレンダリングされた
div
要素。
- unmount :コンポーネントをアンマウントするための
ReactDOM.unmountComponentAtNode
単純なラッパー(例えば、componentWillUnmountのテストを簡素化するため)。
- getByLabelText :ラベルに基づいてフォームコントロールを取得します。
- getByPlaceholderText :プレースホルダーはラベルの非常に優れた代替手段ではありませんが、特定の状況でそれが理にかなっている場合は、それらを使用できます。
- getByText :テキストコンテンツによって要素を取得します。
- getByAltText :
alt
属性の値で要素(の種類)を取得します。
- getByTestId :
data-testid
によって要素を取得しdata-testid
。
これらの各ヘルパーgetメソッドは、アイテムが見つからない場合、情報を提供するエラーメッセージを表示します。 さらに、同様のクエリメソッドのセット(
queryByText
)があります。 要素がない場合にエラーをスローする代わりに、
null
返し
null
。これは、要素がないことをDOMで確認する必要がある場合に役立ちます。
さらに、これらのメソッドに、必要な要素を見つけるために、次を渡すことができます。
- 大文字と小文字を区別しない文字列:たとえば、
lo world
はHello World
と一致します。
- 正規表現:たとえば、
/^Hello World$/
は、Hello World
と一致します。
- テキストと要素を受け入れる関数:たとえば、関数
(text, el) => el.tagName === 'SPAN' && text.startsWith('Hello')
を使用すると、テキストコンテンツがHello
始まるspan
要素が選択されます。
Anto Ryanのおかげで、ライブラリにはJestの仲人がいることに注意してください。
- toBeInTheDOM :要素がDOMに存在するかどうかを確認します。
- toHaveTextContent :指定された要素にテキストコンテンツがあるかどうかを確認します。
まとめ
react-testing-library
の主な機能は、コンポーネントの実装の詳細をテストできるヘルパーメソッドがないことです。 テストとソフトウェア開発のベストプラクティスを促進するテストの開発を目指しています。 反応テストライブラリが役に立つことを願っています。
親愛なる読者! プロジェクトでreact-testing-libraryを使用する予定はありますか?