Reduxテスト:ハード獲得レッスン


新しい進路を祝うConfidently Testing Redux Applications with Jest & TypeScript 私は私が生産反応アプリケーションでreduxを使用して私の長年のテストについて学んだ教訓のいくつかを共有したいと思いました.
私が経験を通して学んだほとんどすべては、他によってすでに発見されてRedux Style Guide 私は非常に読んで、あなたのチームと共有をお勧めします.
特に、私は私が私がたくさんの面倒なしで正しいものをテストしているように感じる場所に着くのを援助した4つのものを共有したいです.リストです.
  • 切断したコンポーネントのテストを停止する
  • 小さなユーティリティライブラリをビルドする

  • 統合スタイルテストを好む
  • あなたのアプリにreduxをもたらすことについての困難なことの一つは、任意のredux接続コンポーネントは<Provider> . あれProvider ツリー内のすべてのコンポーネントが同じReduxストアを使用するようにします.いつのアプリを構築している場合は、通常は<Provider> トップレベルで、それについて心配する必要はありません.それは主要な痛みになるが、テストを行うときにduduxアプリ.Redux接続コンポーネントの各テストは個別に独自のプロバイダにラップされなければなりません.
    多くのチームは、スマートフォンをエクスポートすることによって回避するconnect() EDコンポーネントと同じファイル内の同じコンポーネントの基本(非redux)バージョン.彼らは、ちょうどすべてでredux接続版をテストしないでください.これをしないでください.
    あなたのredux接続されたコンポーネントをテストするのを避けることは2つの理由のための誤りです.最も明らかなのは、お客様がお使いのコンポーネントのバージョンをテストしていないことです.これは、あなたのテストにいくつかの信頼を失うことを意味します.あなたは明らかに重要な場合を残している.次の理由はredux hooks API , これは非常に優れた開発者の経験を提供するconnect() このパターンをサポートしていません.あなたが将来的にあなたのコンポーネントを分離し続けることができないでしょう.
    より良いアプローチは、コンポーネントを設定してレンダリングする方法を簡素化するユーティリティを作成することです.私はコースで3つを使いますrenderWithContext , getStoreWithState , and getStateWithItems . これらのユーティリティは、複雑なセットアップコードを使用してテストを混乱させることなく、状態とコンテキストで動作するのを助けます.
    一番簡単なものから始めましょうgetStoreWithState :
    import { configureStore } from "@reduxjs/toolkit";
    
    const reducer = { /* ... */ }
    
    export const store = configureStore({ reducer });
    
    export function getStoreWithState(preloadedState) {
      return configureStore({ reducer, preloadedState });
    }
    
    ReduxツールキットはconfigureStore 状態をプリロードする方法.The createStore method Reduxでもこのオプションが含まれます.昔は道具に頼るredux mock store テスト用のReduxストアを生成するには、必要はありません.あなたのアプリケーションと正確に同じ減速を含むテストのためのストアを生成することができますが、事前にあなたのテストのために必要などのような状態でロードされています.
    必要な次のユーティリティは、状態とコンテキストを使用してコンポーネントをレンダリングする方法です.私のテストのために、私は通常使用していますReact Testing Library , しかし、あなたが使っているならば、同じアプローチはうまくいきますenzyme .
    import { render } from "@testing-library/react";
    
    export function renderWithContext(element, state) {
      const store = getStoreWithState(state);
      const utils = render(
        <Provider store={store}>
          {element}
        </Provider>
      );
      return { store, ...utils };
    
    私は、Aを含む多くのテストスイートを見ましたmountWithStore 彼らの中に機能するが、私はあなたが利益のトンをアプリの広いユーティリティファイルに移動すると思う.それは一貫して事前に状態を事前に簡単になり、あなたのテストに必要な可能性のある追加のコンテキストを提供します.
    これらの2つのユーティリティで、それはかなり前に任意の状態を事前にロードされたコンポーネントをレンダリングするためにまっすぐ進む.
    import { renderWithContext } from "../test-utils";
    
    test("error banner should appear", () => {
        renderWithContext(<Header />, { errors: [{ /* ...  */ } ] })
        expect(screen.getByRole("alert")).toHaveTextContent("Could not load data");
    });
    
    私は少しこれを改善するために見つけた唯一の他のユーティリティは、あなたのための全体のアプリケーションの状態を生成するものですが、あなたが望むかもしれないいくつかの作品を変更することができます.いくつかのアプリは、役立つことができるJSONファイルにこの状態を置くが、いくつかの共通の部品をオーバーライドすることができますユーティリティ機能を持っている重要な実証済みです.これは常にあなたのアプリケーションにユニークですが、ここではどのようなものの1つの例です.
    export function getStateWithErrors(errors) {
      const state = {
        products: { /* ... */ },
        cart: { checkoutState: "READY", items: {} },
        errors
      };
      return state;
    }
    
    上記のテストは次のように書きます.
    import {
      renderWithContext,
      getStateWithErrors
    } from "../test-utils";
    
    test("error banner should appear", () => {
        const state = getStateWithErrors([{ /* ... */ }]);
        renderWithContext(<Header />, state);
        expect(screen.getByRole("alert")).toHaveTextContent("Could not load data");
    });
    test("error banner should not appear", () => {
        const state = getStateWithErrors([]);
        renderWithContext(<Header />, state);
        expect((screen.queryByRole("alert"))).toBeNull();
    });
    
    このアプローチを使えば、関数が残りの処理をしている間に単一のエラーメッセージを渡す必要がある状態を生成することが容易になります.
    それは私がどのようにユーティリティ機能を私は私のテストを私のreduxアプリのための楽しいテストを書くのを助けることについてのビットは私のテストをより信頼性の低いトリックに頼る必要がないことです.このシリーズの次の記事はです.
    あなたがReduxアプリケーションをテストする私のアプローチについてもっと知りたいならば、見てくださいmy course on egghead.io .