ReactJS-テストの私の理解

私の上司が言うことができるように、みんなに揺さぶってください。 私はもっ​​と賢いものを思いついていないので、そこでやめます。



実際、この資料は必ずしも他の人に何かを教えるふりをしているわけではありません。 おそらく、コメントで十分に収集して、代わりに自分自身で学習します)タスクをここで説明します。



私は数ヶ月にわたってリアクションに取り組んできました。基本的には、私のバックグラウンドがバックであり、ここでは非識字を排除するようなものです。 Reduxおよびその他のサポートコンセプトはまだ方程式に導入されていません。



作成された小さなアプリケーションをテストしようとするタスクがありました。 まあ、すべての種類のサービスは、ジャスミンを使ってなじみのあるスタイルでテストできます。 単体テストの概念のフレームワーク内にとどまりたい場合、コンポーネントの方が難しいです。 アイデアは、実装ではなく、受け入れられた契約をテストすることです。つまり、テストは「ボタンを突く-アプリケーションがこれを試みた」ように見えるはずです。



まあ、私は紹介と結び付けます。



1。



ユーザーのアクション(またはタイマーなど)に対するコンポーネントの応答は2つあります。それは、内部で何らかの変更を行うことも、他の何かを変更することもできます(SPAの別のページまたは別の部分に移動し、ファイルをダウンロードします)。 ..)。 ReactJSの場合、コンポーネントの状態の変更、またはイベントの発生に関する親コンポーネントへの通知のいずれかによって「内部的に」正しく実装されます(親が他の小道具でコンポーネントを再レンダリングできるように)。 そして、「私たちの外」での変更も、親がコンポーネントにドロップする特定の関数を呼び出すことによって実装されます。古典的な意味ではイベントハンドラーであるか、アクションを実行する「デリゲート」です(たとえば、別のページに移動します)。 これまでのところ、私はこのようなことは通常ReactJSの下で行われているという印象を持っています。



テストの反応は、「ユーザーのアクションを模倣し、どのメソッド(注入されたメソッドから)およびコンポーネントが呼び出したパラメーターをチェックする」に限定されることがわかりました。 本当に、コンポーネントにsetStateを注入しない瞬間があります。 つまり、setStateをインターセプトする方法を理解する必要があります(一般的に、同じジャスミンがこれを処理できるようです)、またはsetStateの代わりに、コンポーネントに状態を変更する他の方法を与えます。 これに少し戻って、理由を明らかにします。



2。



疑問は残っていますが、実際には、ユーザーの行動をどのようにシミュレートするのか。 私はインターネットを少し読んで、1) ここで -コンポーネントのパブリックAPIでincrementなどのメソッドを出力し、component.getInstance()、2)を介してそれらを呼び出すことを提案します -そして、いくつかの基準に従って構築されたツリーでコントロールを探して押します。 2番目の方法は、テストが通常テストのロジックに必要ないマークアップにアタッチされている(そして、この方法でマークアップに不必要な依存関係を作成し、テストの本質から気を散らす)という点と、完全に正しいわけではない(実際にはアクションユーザーは多くの場合、一度に複数のイベントによってトリガーされ、それらのコンポーネントが1つのことだけに関心がある場合でも、「不完全な模倣」を行うことはなんとなくいです)。 1つ目は、まず、パブリックAPIに増分を表示する理由がないためです(コンポーネントは、小道具の注入など、反応に必要なもの以外のAPIを持っている必要はありません)。 {increment}よりも複雑です。たとえば{()=> if(this.state.count> 0)decrement();}の場合、この追加のバインディングはテストしません。



これまでのところ、合理的な答えを得るためには、正しい視点を選択する必要があるように思えます。 マークアップ内の重要なプロセッサーは破棄する必要があります。 それらは簡潔さの観点から魅力的であり、イベントの「内部」解釈(+ボタンをクリック)を、コンポーネントの割り当て(カウンターインクリメントの呼び出し)に関する解釈にすぐに変換できますが、このための別の方法を排除することはできませんが、これはテスト容易性に影響します。 incrementの例では、incrementを呼び出すことでユーザーアクションをシミュレートするのは正しくありません。incrementはコンポーネントの目的に関して既に表されたユーザーアクションであり、コンポーネントの契約(チェックする)は通常「このボタンがクリックされたとき、次に「; したがって、契約の一部であるイベントは「+ボタンを押す」ことであり、「カウンタ増分コマンド」ではありません。



そして、私たちが契約の一部として認識するイベント以来、突然彼らは公開される権利を持ちます。 つまり、実際には、コンポーネントはマークアップとコントローラーに分割され、それらを個別にテストします。 したがって、コントローラーには独自のAPIがあり、これはマークアップから表示されるため、パブリックである必要があります。 クラスを(コンポーネントの作成に基づいて)コントローラーのタスクと正確に考えると、このクラスはこれを公開できます。 つまり、コントローラのこれらの「制御信号」を「getInstance()。onPlusButtonClick()」を介して呼び出すことは非常に合理的です。 確かに、一般的なケースでは、入力にフィードされるイベントオブジェクトを作成する必要があります(そして、完璧主義の考慮事項から-多かれ少なかれ正しい)。 しかし、多くの場合これは回避できます。マークアップにイベントを直接書き込まないでください。ただし、(event)=> onTextChange(event.value)のようなものはおそらくテストされないほど無害に見えますが、その後、イベントではなく直接テキストを信号入力に送信することができます。



しかし、これはすべて雲の中に浮かんでいる可能性があります。コンポーネントが小さくシンプルな場合、マークアップに直接何かを入れて書いてからボタンを見つけて突くのは簡単です。 上記で提案したことは目に見える不快感をもたらすべきではないようですが、本質的には、テストを除き、ここで行われた決定は何にも影響を与えず、テストの可愛さはおそらくそれほど重要ではありません。開発者の自由。」 進歩的な大衆が書いているものを見てみましょう:)



3。



しかし、生成されたマークアップは、結局、コンポーネントコントラクトの一部でもあります=)しかし、ここでも問題は-どの程度までですか? 部分的に、マークアップは単なる実装です。 重要なマークアップと重要でないマークアップをマークアップで区別する方法は明確ではありません(CSSの設計仕様を作成することに加えて)。 原則として、マークアップ全体が契約と見なされる場合、jestは標準と比較して回帰テストを提供します。 しかし、マークアップのどの部分が重要であるかがわかっている場合は、生成されたDOMを分析することでそれらをテストできます。 これは非常に詳細な分析です。 これまでのところ、私はまだ標準と比較する傾向がありますが、これはあまりきれいではありません。



マークアップを分析する方法の開発は、マークアップをテストするために解決する必要がある唯一のタスクではありません。 何らかのアクションの後、特定の作業の瞬間にコンポーネントがどのように見えるかをテストしています。 そして、同じテスト中にアクション自体を開始することはあまり正しくありません(少なくとも-これを行う方法が明確であっても)。 マークアップは状態関数と小道具を計算した結果であるため、これらの以前のアクションの実装を模倣するような状態と小道具を与えるだけでよいようです。 つまり、テストは「アプリケーションの2番目のタブが選択され、参照データセットの2番目のページが表に表示されたときにマークアップがどのように見えるかを確認する」として定式化されます。 そして、ここで問題が発生します、テストでそのような状態をどのように記述するか:1)テストはコンポーネントの状態構造をどのように知るか、これは実装の一部であり、コントラクトではないため、2)正しい状態をどのように形成すべきか(単純な列挙によるオブジェクトの制御されていない作成)プロパティと値、またはテスト用ではなく、実生活用に提供される必要があります-コンポーネントが形成された状態の正確さを保証する何らかのビルダー)。



プライバシーに関しては、視点が再び生じます。 コンポーネントの状態がブラックボックスである場合、親は子コンポーネントの状態をまったく処理しないか、状態を読み取ったり変更したりできる機能へのアクセスを子に提供しますが、彼自身も状態の構成については知りません。 しかし、MVVMパラダイムの.Netで使用されているものと同様の別のアプローチが可能です。この場合の状態は、ビューを記述する特定のViewModelモデルであり、このビューのコンポーネントは、それらに関係するこのモデルの部分に添付されます。 次に、ViewModel構造は自己価値があります。それを管理し、コンポーネントの状態を制御し、読み取ります-コントロールによって保存された状態を読み取ります。 そして、当然、ViewModelプロパティをパブリックにします-すべての子コントロールがモデルに自由にアクセスし、どこからでも読み書きできるという意味ではなく、ViewModelが格納されている最上位レベルで、各コンポーネントの状態がどのように見えるか(どのプロパティがどのフォーマットで記述されているか)、テストを含めて、コンポーネントのレンダリング方法を確認する状態を設定できます。



上記のパート1の最後で、コンポーネントがsetStateの代わりに他のメカニズムを使用する場合のオプションについて説明しました。説明したモデルはこのアプローチの良い例です。 ViewModelはどこかに保存され、子コンポーネントにはpropsの一部が与えられます。そのため、コンポーネントがViewModelのXプロパティに作用できるように、setX some f(x)と呼ばれるpropsに渡すことができます。 := x。 もちろん、実際には、f(x)はトリッキーでなければなりません-同期的に状態を変更するだけでなく、setStateのような動作をします。 オプションの1つとして、おそらくコンポーネントの最上位に実際の状態を設定し、この最上位コンポーネントのsetStateを介して実装されるアクセサを子にさせることができます。 別のオプションは、Reduxのようなよく知られた外部ストレージメカニズムです。



しかし、保証された正しい状態を形成する方法-私はまだそれを考えていません。 テストだけのことなら、神は彼と共におられるでしょう。 しかし、ViewModelは一般に知られている構造を持ち、ビューの外部(この場合はテストから)に変更を加えることができるため、正式な観点からは、それ以上のパラメーターを受け取らないような状態操作メソッドを提供するのが良いでしょう一貫性のある状態にする必要があります。 gotoFirstPage()のようなものは、それ自体が現在のページ番号が1であるべきことを理解し、この場合「前のページ番号」をnullに設定する必要があることも知っています(発明された例)。



All Articles