Reactコンポーネントのテスト

今日翻訳している記事の著者であるOvidiu Cheshesesは、数千のユーザーインターフェイステストを作成しました。 彼は、テストによって開発者が自分のプログラムが期待どおりに機能し、修正および拡張された後も仕事を続けるという開発者の信頼を高めるはずだと言います。 ただし、ユーザーインターフェイスのテストで自信が得られることはほとんどありません。 その代わりに、多くの場合、プログラマーの仕事の非生産的なことについての失望や思考につながります。



画像






彼は、Reactで書かれたインターフェースのテストの問題を提起したと言います。なぜなら、彼はこのフレームワークの普及の数年前にこのプロセスに典型的な問題に遭遇し、それらを解決するのに多くの時間を費やしたからです。 彼は、Reactコンポーネントを作成するのと同じくらい簡単にテストできるツールと作業方法を見つけたと考えています。



彼は、テストへの正しいアプローチを見つけるために理解することが重要な2つの基本原則からストーリーを開始します。次に、例を示し、Reactアプリケーションをテストする必要のある人の生活を大幅に簡素化できる1つの便利なツールを紹介します。



原則1:抽象化の破産コンポーネント= f(小道具、状態)を考慮する



理論的には、タイトルに示されているデザインは美しく見えますが、この抽象化に基づいて実際のコンポーネントをテストしようとすると、すべてがおかしくなります。 コンポーネントを個別にロードしようとすると、すぐに次のことが明らかになります。



component=f(props, state, context)
      
      





または、より正確には、次のようになります。



 component=f(props, state, Redux, Router, Theme, Intl, etc)
      
      





しかし、そのような設計でさえ現実にはほど遠い。 つまり、これについて話している:



 component=f(props, state, context, globals)
      
      





もちろん、これらはグローバル変数ではなく、おそらく一部のモンスターだけがそれらを使用するでしょう。 グローバルAPIを意味します。



 component=f(props, state, context, fetch, localStorage, window size)
      
      





ご覧のとおり、現実は理論よりもはるかに興味深いものです。



Reactコンポーネントのテストは、複雑な問題を常に解決するビジネスです。 しかし、それについて話す人はほとんどいません。 公式のテスト例は、最も単純なコンポーネントで示されていますが、コンポーネントの世界の「モンスター」に関しては、基本的に何も書きません。 本日は、これらのモンスターを目で見て、JestやEnzymeなどの既存のツールとうまく組み合わせて、あらゆるコンポーネントを「緩和」できる新しいシンプルなAPIについてお話します。



上記のテキストはスピーチの断片で、私は何らかの形で会議に参加したいと考えていました。 私はそこに着きませんでした(悲しい音楽をここに含めるのが適切です)が、ここのすべての言葉が重要であり、私はまだそれについて話したいです。



次の例では、デフォルトでJestとEnzymeが使用されていますが、これから説明する手法を適用するためにどちらも必要ではありません。



簡単なテスト例



遭遇する可能性のあるテストの一般的な例は非常に簡単です。 おそらく、これはコールバック関数を持つボタンです:



 const UnrealisticComponent = ({ onReply }) => ( <button onClick={() => onReply('Ja')}>Alles gut?</button> )
      
      





次に、コンポーネントが期待どおりに機能するかどうかを確認しましょう。



 const onReply = jest.fn(); const wrapper = shallow(<UnrealisticComponent onReply={onReply} />) test('kindly asks if everything is alright', () => { expect(wrapper.text()).toBe('Alles gut?') }) test('receives positive response upon click', () => { wrapper.find('button').simulate('click') expect(onReply).toHaveBeenCalledWith('Ja') })
      
      





ご覧のとおり、ここではすべてが非常にシンプルで明確であり、コンポーネントは非常に簡単です。 ただし、実際のコンポーネントをテストしようとすると、すべてが変わります。 認証に使用される最も単純なコンポーネントを想像してください。ユーザー名を入力するフィールドとデータを送信するボタンがあります。 フィールドに名前を入力し、ボタンをクリックすると、入力されたデータでfetch



リクエストが実行されます。 要求は完了し、名前はReduxリポジトリに転送され、 localStorage



キャッシュされlocalStorage



。 一般に、これもすべてそれほど難しくありません。



次に、テストレンダリングのためにこのコンポーネントを送信してみましょう。 残念ながら、テストは開始されません。 以下が表示されます。



 Could not find "store" in either the context or props of Connect… ReferenceError: fetch is not defined ReferenceError: localStorage is not defined
      
      





通常、そのような瞬間に、プログラマーは助けを求めてより高い勢力に訴えます。



ここで、この例は条件付きであり、深刻なアプリケーションでは、認証のためのいくつかの抽象化が提供されることをテストできます。 ただし、すぐにわかるように、これはコンポーネントのテストとは関係ありません。 開発中のアプリケーションのマルチレベルのテストに使用する場合、これらのテストを作成する機能が必要です。それらを常に書き換える必要はありません。



現時点では、経験に基づいて、上記の例について2つの真剣なコメントを作成できます。



  1. 本当にコンテナをテストしていますか?

  2. コンポーネントはグローバルAPIと対話しますか(フェッチなど)?



はい どうして? また、浅いレンダリングを使用しないため、次の原則が得られます。



原則番号2:アプリケーションの補助要素の危険性を覚え、そのコンポーネントをリンクする



コンポーネントは、単純なReactコンポーネントモデルを使用している場合でも、通常の機能とはかけ離れた複雑なエンティティです。 したがって、それらを個別のモジュールに分離し、他のシステムから可能な限り分離するのは魅力的です。 ただし、これらのモジュールが小さいほど、プロジェクトをリンクするためにより多くのコードが必要になり、統合エラーのためにより多くの余地が開かれます。



たとえば、コンポーネント自体を排他的にテストするために、通常、ラッパーなしでコンポーネントをエクスポートします。 高次のコンポーネントはすでにテストされており、その内部メカニズムをテストできるため、この状態は正常と見なされます。 レデューサー、アクション、セレクターをテストします。 私が言えることは、 mapStateToProps



mapDispatchToProps



でさえもテストして、プロジェクトコードがテストで完全にカバーされていることを確認する必要があるということです。 私は、やがて同様のアプローチに従いましたが、一度その主な欠点を理解しました。 ここにあります:





快適さを求めて、きちんとした小さなモジュールに目を向けるのは魅力的です。 しかし、アプリケーションの基本的な部分のみをテストすると、プロジェクトの複雑さ、つまり機能する非常に関連性の高い部分を見失うことになります。 さらに、プログラマーは、コンポーネント統合の検証に関する懸念を、品質管理に関係する他の専門家に伝えるという魅力的なアイデアに屈する可能性があります。 ただし、エンドツーエンドのテストは、はるかに高いレベルで機能する「エアバッグ」です。



アプリケーションを接続するサブシステムの数が少ないほど、コンポーネントテストの制限外になり、テストが正しいこと、さらにアプリケーションを構成するモジュールの再利用性が確実になります。 実際のモジュールとは、たとえば、誰かに転送して再利用することを計画しているモジュールです。 エンドユーザーにとって特定の意味を持ちます。 テストが必要なのはそのような構造であり、テストが簡単なものではありません。



ただし、テスト環境のセットアップの複雑さについてはどうでしょうか?



コンポーネントのテスト環境について



テストを含むファイル内のコードの大部分と、それらを準備するためのほとんどの作業には、「スマート」コンポーネントのプロバイダーとレイアウトデータの設定が必要です。 ステートメントを記述することは、コンポーネントを接続した後、些細なことです。 この点に関して、質問があります。テスト環境のセットアップを簡素化し、開発者が主にステートメントの作成に対処してコンポーネントの動作をテストする機会を与える方法ですか。 コンポーネントの状態をシミュレートする簡単な方法がある場合にのみ、いくつかの条件付きデータを入力に適用します...



そして、実際、そのような方法があります。 Cosmosライブラリフィクスチャは、入力値をシミュレートし、状態の任意の組み合わせでコンポーネントをレンダリングするように設計されています。 また、これはすべてPlaygroundインターフェイスでのみ長い間使用されてきたため、Cosmosを使用すると、テスト環境の複雑な設定を置き換えることができることが明らかになります。 結果として、私たちの前には、2対1の文のようなものがあります。





コスモスの遊び場



コスモスライブラリ機能



最初に、JSXタグの外観(またはReact.createElement



コマンドのReact.createElement



)を思い出しましょう。



 <Button disabled={true}>Click me maybe</Button>
      
      





私たちの前に発表があります。 この場合、次のように{ disabled: true }



プロパティ{ disabled: true }



と子オブジェクトClick me maybe



持つButton



要素(ボタン)が必要Click me maybe







コスモスのインフラ要素は、「ステロイド充填」Reactコンポーネントのようなものと考えることができます。 このような要素は、コンポーネント、プロパティ、および子オブジェクトに関する情報に加えて、ローカル状態、Reduxの状態、またはルーターのURLに関するデータを受信できます。 さらに、インフラストラクチャ要素は、 fetch



XHR



またはlocalStorage



模倣できlocalStorage







これらの機能はすべて、インフラストラクチャ要素をコンポーネントの状態をシミュレートするためのプラットフォームにするプラグインを介して利用できます。



インフラストラクチャ要素は、次のような通常のJSオブジェクトです。



 { props: {} url: '/dashboard', localStorage: {   name: 'Dan' }, reduxState: {}, fetch: [   {     matcher: '/api/login',     response: {       name: 'Dan'     }   } ] }
      
      





このようなコードの記述に慣れると、コンポーネント指向の開発ツールが手に入るだけでなく、コンポーネントのテストを記述するタスクが大幅に簡素化されます。 最近リリースされたCosmos Test APIは、これらすべてを支援します。



たとえば、Cosmos Test APIを使用したCosmosテストは次のようになります。





コスモスでのテスト



また、APIの操作方法は次のとおりです。 試す前に、 ドキュメントを読むと役立ちます



 import createTestContext from 'react-cosmos-test/enzyme'; import fixture from './logged-in.fixture'; const { mount, getWrapper, get } = createTestContext({ fixture }); beforeEach(mount); test('welcomes logged in user by name', () => { expect(getWrapper('.welcome').text()).toContain('Dan'); }); test('redirects to home page after signing out', () => { getWrapper('.logout-btn').simulate('click'); expect(get('url')).toBe('/'); });
      
      





これはすべて、最小限の数の補助要素で標準のテストアプローチに準拠するように行われます。 ここで使用されている命名規則、この「コンテキスト」に完全に満足しているわけではありませんが、APIが多くのプログラマーによってテストされ、現在ScribbleLiveでテストされていることを報告できて嬉しいです。



そしていよいよ、最後に、私の実践からいくつかの興味深い事例をお話しします。



そのため、コードベースが確立された既にかなり成熟したプロジェクトであっても、新しいアプリケーションコンポーネントを実装するときには、通常、特殊なプロトタイプを作成します。 最初からReduxリポジトリーに接続する代わりに、ローカル状態から作業を開始して、新しい機能を作業レベルにすることができます。 私は何度も完全にテストされたプロトタイプに行き、単一のテストを書き直すことなく、ローカル状態をReduxに移植しました。



かつて、ステートレスForm



コンポーネントは多くの画面で使用されていました。 これは優れた抽象化ですが、各インスタンスにはデータマッピングテンプレートとそのライフサイクルに関連する手順が必要でした。 プロジェクトにはリファクタリングが必要でした。 これらのフォームで操作を繰り返すための抽象化を作成し、 FormConnect



という名前を付けFormConnect



。 その後、1つのテストを書き直すことなく、12を超える画面を作り直しました。 なんで? なぜなら、彼らとのプログラムによる作業の観点から、これらの画面は変更されていないからです。



まとめ



ご覧のとおり、Cosmos APIを使用すると、Reactプロジェクトのコンポーネントの複雑なテストの分野で状況を大幅に改善できます。 さらに、システムを変更する際にテストを継続的に書き換える必要がない実装では、プロジェクトの柔軟性と作業の利便性が考慮されます。 マテリアルの作成者とCosmos APIのアイデアが、Reactアプリケーションのテストに役立つことを願っています。



親愛なる読者! Reactを使用して作成されたプロジェクトをどのようにテストしますか?



All Articles