コンポーネントインスタンスを用いた反応試験ライブラリのテスト


昨年は、業界の動向に従い、自分の反応成分を試してみるEnzyme 抱くTesting Library .
変更は良いされている!現在、私のテストは、より簡潔で、明確で、意味があります.コンポーネントの内部に関連するコードを持っていないので、特に読みやすくなります.そして、私は1つのテストが多くの構成要素の間でインタラクションをカバーすることができる方法が好きです.
でも.時々私は酵素を欠場する.

反応コンポーネントインスタンスをテストする理由


テストライブラリに切り替えるとき、私たちはUIに焦点を当て、私たちの反応コンポーネントの内部実装との任意の接触を避けるためにしようとします.私たちのテストは最終的なユーザーのようになります.そして、我々のアプリだけで動作する必要がありますどのように物事はバックグラウンドで処理されます.
私は、私の同僚と、何との違いについても議論しましたSelenium or Cypress テスト?何かあるべきか
我々のシステムインテグレーションをチェックするために、Selenium/Cypressと一緒に作成したEnd - to - Endテストの違いがたくさんあります.そして、我々の深い反応テストは、サーバーやAPIの相互作用を全く無視することなく、仕様に従うべきです.しかし、私はいくつかの重複がある方法を参照してください、私は混乱している人々を理解し、特に私たちは、UIとしてのみ私たちの反応コンポーネントを参照してください.
反応コンポーネントはUIよりも、彼らはまた、より良い単位テストよりも、ボタンをクリックしてユーザーをエミュレートテストすることが困難なロジック、機能が含まれています.

テストの爆発


なぜ時々私たちのテストのコンポーネントのインスタンスにアクセスするのがいいの例を示す前に、私は共有しますreact-dom-instance , DOM要素にバインドされたコンポーネントインスタンスを見つけるライブラリ.再酵素を再インストールする必要なしで、それは反応テストライブラリで本当にうまく動きます.
テストライブラリを使用してテストを作成するとき、私は頻繁につまずく問題があります.私の論理が親コンポーネントと多くのステキティブな子コンポーネントにあることは、親が私が期待することをチェックするために複数回対話する必要があることです.
我々はこのtodoリストのアプリを持っていると言うFumie Wada ), コンポーネントによってレンダリング<TodoApp /> :

右上の「新しいアイテムを作成」リンクをクリックすると、フォームが開いて新しいTODO項目を入力できます.フォームは<CreateForm /> コンポーネント

ユーザーがフォームを開くと、項目のテキストを入力し、それが重要かどうかチェックし、色を選択し、“追加”ボタンをクリックし、我々のTodoApp コンポーネントは
  • テキストを入力し、選択した色で新しいtodo項目を作成します.
  • それが重要であるとマークされるならば、アイテムはもう一つのリストに加えられます.
  • それが重要としてマークされていない場合は、選択した色でGoogle Analyticsにイベントを送信する🤷
  • 我々の中でTodoApp.spec.js ファイルには、次のようなロジックをテストする必要があります.
    it('should create an important item', () => {
      const { queryByTestId } = render( <TodoApp /> );
    
      fireEvent.click( queryByTestId('openButton') );
      fireEvent.input( queryByTestId('todoInput'), {target: {value: "Buy some bread"}} );
      fireEvent.click( queryByTestId('color_red') );
      fireEvent.click( queryByTestId('importantCheckbox') );
      fireEvent.click( queryByTestId('addButton') );
    
      // We had mocked some actions beforehand
      expect( createItem ).toHaveBeenCalledWith( "id1", "Buy some bread", "red" );
      expect( addToImportant ).toHaveBeenCalledWith( "id1" );
      expect( trackGAEvent ).not.toHaveBeenCalled();
    });
    
    我々はそこでmuchoをテストしています、そして、よりよくされることができた多くのものがあります、しかし、ちょうど例のために彼らを無視してください.
    どのように我々は子供のコンポーネントでクリックした色に焦点を当て、それは私たちがtodoを作成している色です.我々は、フォームで多くの色を持って、我々はすべてのテストする必要がありますか?
    「マークとして重要」オプションを有効にしていない場合は、Google Analyticsのすべての色を正しく追跡していることを確認してください.TodoApp コンポーネントテストでは、色がどれくらいの色であるかを気にするべきではありません.すべての色をテストしないだけで悪い感じが、すべてのクリックし、我々はすべてのテストのために行う必要があります入力タイピングも非常に繰り返し.
    入れ子にされたコンポーネントが一緒に働いてテストするための反応テストライブラリの能力は驚くべきです、しかし、それは反応木のトップにチェックを動かす傾向があります.子供たちが多くのセッティングを持っているとき、我々は一番上の構成要素で本当に大きいテストファイルで終わります、そして、それらのテストは通常我々がしなければならないチェックの小さい順列で繰り返しの仕事によって作られます.子コンポーネントの中には、親コンポーネントのテストケースの成長率が大きくなります.それはテストの爆発のようです.

    インスタンスを使用したファイルの分割テスト


    The CreateForm コンポーネントは、複雑なロジックを持っていない、単にユーザーのタイプの名前を選択し、それが重要である場合、選択し、色を選択します.それは、その情報とは何かを知らないが、例えば、どのように多くの色を選択することができますの責任があります.
    我々は、ユーザーの選択に感謝することができますonAdd propのためのテストファイルCreateForm 利用可能なすべての色をテストするのに最適な場所に見えます.
    it('should select the color red when clicking on the red input', () => {
      const onAddMock = jest.fn();
      const { queryByTestId } = render(
        <CreateForm onAdd={ onAddMock } />
      );
    
      fireEvent.click( queryByTestId('color_red') );
      fireEvent.click( queryByTestId('addButton') );
    
      expect( onAddMock.mock.calls[0].color ).toBe('red');
    });
    
    // ...the same for the rest of the colors
    
    それはシンプルでよくスコープテストです.かつて、我々は個々にすべての色をテストしましたCreateForm , 我々は再びそれらをテストする必要はありませんTodoApp .
    我々は、レンダリングされたCreateForm インスタンスはどのような色に関係なく提供し、すべてのボタンをクリックせずにロジックをチェックしますが、コンポーネントが適切に統合されていることを確認してください.
    import { findInstance } from 'react-dom-instance';
    
    it('should create an important item', () => {
      const { queryByTestId } = render( <TodoApp /> );
      const createForm = findInstance( queryByTestId('createForm') );
    
      // call the `onAdd` handler passed by `TodoApp` directly
      createForm.onAdd({
        text: "Buy some bread",
        color: "whatever",
        isImportant: true
      });
    
      // We had mocked some actions beforehand
      expect( createItem ).toHaveBeenCalledWith( "id1", "Buy some bread", "whatever" );
      expect( addToImportant ).toHaveBeenCalledWith( "id1" );
      expect( trackGAEvent ).not.toHaveBeenCalled();
    });
    
    TodoApp テストは、“私は”私はそれがどのように内部的に動作しない気にしない、クリックして物事をクリックして“私はユーザーがクリックして何を気にしない”を参照してください、私はこれを受け取ることを期待.我々はまだコンポーネントの内部を使用していませんが、我々は彼らの表面、APIを知って最大限に活用.
    それは我々がユーザーのクリックをテストしていないことではない、それは私たちは正確なユーザーの相互作用に依存すべきではない場所でテストを繰り返す必要はないということです.このアプローチにはいくつかの利点があります.
  • テストケースはソースコードの他にスコープが良いです.
  • 変化CreateForm 破れないTodoApp テスト、少なくとも、我々はそれに複数の変更を必要としません.
  • UIオプションを追加するときにテストケースの指数関数的に成長した親コンポーネントのための大きなテストファイルはありません.
  • ない悪い悪い?

    ときに我々の反応テストでインスタンスを使用する


    大きなテストファイルを分割するには、どのようにインスタンスをテストする方法で便利なコンポーネントの良い例です.コンポーネントの命令的なメソッドをテストするような他のケースもあります.
    しかし、我々が我々のテストでインスタンスを使うことができるという事実が我々が至る所でそれをするべきであるというわけではないということを心に留めておいてください.これは味の問題ではなく、「私はユーザーとの対話を好む」または「i好みのインスタンス」を好む.
    それは我々がユーザーの相互作用をエミュレートすることによって一緒に構成要素のグループの深いテストを行うことができるスイートスポットを見つけることについてです.
    私は、“スイートスポットを見つける”と言うと、インスタンスを使用するときには役立ちません知っているが、それは難しいことではない.私たちは皆、テストファイルが制御不能になっていることを知っています.その時点で、独立した機能単位を識別し、独自のファイルにテストを展開します.ちょっとした練習をすれば、分割するのはいい考えだとすぐに予言することになります.
    これはDEV . TOの私の最初の記事であり、私はそれを書いて楽しんだ!あなたがそれを好きなら、私に従って、私に愛とユニコーンを与える、と私ははるかに多くを書くよ!
    私のTwitterは.