彼は、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つの真剣なコメントを作成できます。
- 本当にコンテナをテストしていますか?
- コンポーネントはグローバルAPIと対話しますか(フェッチなど)?
はい どうして? また、浅いレンダリングを使用しないため、次の原則が得られます。
原則番号2:アプリケーションの補助要素の危険性を覚え、そのコンポーネントをリンクする
コンポーネントは、単純なReactコンポーネントモデルを使用している場合でも、通常の機能とはかけ離れた複雑なエンティティです。 したがって、それらを個別のモジュールに分離し、他のシステムから可能な限り分離するのは魅力的です。 ただし、これらのモジュールが小さいほど、プロジェクトをリンクするためにより多くのコードが必要になり、統合エラーのためにより多くの余地が開かれます。
たとえば、コンポーネント自体を排他的にテストするために、通常、ラッパーなしでコンポーネントをエクスポートします。 高次のコンポーネントはすでにテストされており、その内部メカニズムをテストできるため、この状態は正常と見なされます。 レデューサー、アクション、セレクターをテストします。 私が言えることは、
mapStateToProps
や
mapDispatchToProps
でさえもテストして、プロジェクトコードがテストで完全にカバーされていることを確認する必要があるということです。 私は、やがて同様のアプローチに従いましたが、一度その主な欠点を理解しました。 ここにあります:
- テストには実装よりも多くの労力が必要です。 ここでは、新しい開発者のみに幸運を祈ることができます。 初日は、最初のコンポーネントを作成し、テストを作成するにはさらに3日間必要です。
- テストはリファクタリングを防ぎます。 私にとって、これはおそらく主な問題です。 私たちが言うように、コードを書く過程で、私たちはしばしば独自の墓を掘ります。そして、典型的なプロジェクトを厳格な不器用な構造に変えるには、各関数の綿密なテスト以上に良い方法はありません。
- エラーの可能性を高めます。 いくつかの些細なこと(ある種のセレクターなど)をテストすることを忘れるだけで十分であり、コンポーネント全体は正常に動作しませんが、既存のテストに正常に合格します。
- 非同期入力および出力。 コンポーネントは一部のプロパティに正しく応答でき、
mapStateToProps
は特定の状態に基づいて正しいデータを生成できますが、入力データと出力データが互いに対応していない場合、エクスポートされたコンポーネントは失敗します。
快適さを求めて、きちんとした小さなモジュールに目を向けるのは魅力的です。 しかし、アプリケーションの基本的な部分のみをテストすると、プロジェクトの複雑さ、つまり機能する非常に関連性の高い部分を見失うことになります。 さらに、プログラマーは、コンポーネント統合の検証に関する懸念を、品質管理に関係する他の専門家に伝えるという魅力的なアイデアに屈する可能性があります。 ただし、エンドツーエンドのテストは、はるかに高いレベルで機能する「エアバッグ」です。
アプリケーションを接続するサブシステムの数が少ないほど、コンポーネントテストの制限外になり、テストが正しいこと、さらにアプリケーションを構成するモジュールの再利用性が確実になります。 実際のモジュールとは、たとえば、誰かに転送して再利用することを計画しているモジュールです。 エンドユーザーにとって特定の意味を持ちます。 テストが必要なのはそのような構造であり、テストが簡単なものではありません。
ただし、テスト環境のセットアップの複雑さについてはどうでしょうか?
コンポーネントのテスト環境について
テストを含むファイル内のコードの大部分と、それらを準備するためのほとんどの作業には、「スマート」コンポーネントのプロバイダーとレイアウトデータの設定が必要です。 ステートメントを記述することは、コンポーネントを接続した後、些細なことです。 この点に関して、質問があります。テスト環境のセットアップを簡素化し、開発者が主にステートメントの作成に対処してコンポーネントの動作をテストする機会を与える方法ですか。 コンポーネントの状態をシミュレートする簡単な方法がある場合にのみ、いくつかの条件付きデータを入力に適用します...
そして、実際、そのような方法があります。 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を使用して作成されたプロジェクトをどのようにテストしますか?