$30 off During Our Annual Pro Sale. View Details »

React でコンポーネントを利用したテストをゴリゴリ書く

bom-shibuya
November 06, 2023

React でコンポーネントを利用したテストをゴリゴリ書く

2023/11/06 ZOZO Tech Meetup - Webフロントエンド

bom-shibuya

November 06, 2023
Tweet

Other Decks in Programming

Transcript

  1. React でコンポーネントを利用し
    たテストをゴリゴリ書く
    株式会社ZOZO

    ZOZOTOWN開発1部 WEBフロントエンドブロック

    渋谷 拓正
    Copyright © ZOZO, Inc.
    1

    View Slide

  2. © ZOZO, Inc.
    株式会社ZOZO
    ZOZOTOWN開発1部 WEBフロントエンドブロック
    渋谷 拓正
    Web 制作や CRM サービスの Web アプリの開発に関
    わった後、2023年4月に ZOZO に入社。
    プロテインを朝晩摂取したり絵を描いたりしています。
    子供と遊んで毎日過ごしています。
    2

    View Slide

  3. © ZOZO, Inc.
    https://zozo.jp/
    3
    ● ファッションEC
    ● 1,500以上のショップ、8,900以上のブランドの取り扱い
    ● 常時95万点以上の商品アイテム数と毎日平均2,900点以上の新着
    商品を掲載(2023年6月末時点)
    ● ブランド古着のファッションゾーン「ZOZOUSED」や
    コスメ専門モール「ZOZOCOSME」、靴の専門モール
    「ZOZOSHOES」、ラグジュアリー&デザイナーズゾーン
    「ZOZOVILLA」を展開
    ● 即日配送サービス
    ● ギフトラッピングサービス
    ● ツケ払い など

    View Slide

  4. © ZOZO, Inc.
    4
    3 年前にコンポーネントではなく Hook 自体をテストしたいというモチベーションから「React Hooksで
    テストをゴリゴリ書きたい」という記事を書きました。
    はじめに
    記事URL: https://zenn.dev/bom_shibuya/articles/5c3ae7745c5e94

    View Slide

  5. © ZOZO, Inc.
    5
    はじめに
    ● 当時の考え
    ○ hook やコンポーネントで使われる関数がそれぞれ正しく動いている
    ■ コンポーネント内の実装はそれらを組み合わせている部分が大きい
    ● ある程度正しいことが担保されているのでは?
    ○ コンポーネントは JSX が書かれている
    ■ DOM 構造と結びついているので壊れやすいのでは?
    ● それから3年経って今どのようなことを考えているか
    ○ => 今日のお話 🔥

    View Slide

  6. © ZOZO, Inc.
    6
    今日話すこと
    ● コンポーネントを利用したテストを書く
    ● カスタムフックのテスト
    テストの具体的な書き方ではなく、どの様な考えでテストを書くかをお話しできればと思っています 🙌

    View Slide

  7. © ZOZO, Inc.
    こんな経験はありませんか?
    7

    View Slide

  8. © ZOZO, Inc.
    8
    こんな経験はありませんか?
    ● Hook のテストは書いたがコンポーネントにあるロジックはテストしていない
    ● 修正のたびにテストも直す必要がある
    ○ 毎回直さなければならないので、そもそもテストを書くのが面倒に感じる、、、
    ● 何度も同じようなテストコードを書いている気がする
    ○ 別のテストファイルで同じようなテストを書いた気がする、、、

    View Slide

  9. © ZOZO, Inc.
    コンポーネントを通してテストを書く
    9

    View Slide

  10. © ZOZO, Inc.
    10
    public なメソッドをテストする
    ● 実装の詳細をテストしてしまっているのかも
    ● private なメソッドはテストを書かない
    ■ private なメソッドは public なメソッドを通して必ず利用されているはず
    ■ 参考: t-wada 「プライベートメソッドのテストは書かないもの?」
    ● React にとって public な関数とは?
    ○ => コンポーネント
    ○ ある機能を満たす形でディレクトリが切られそこから 1 つコンポーネントが export される
    ■ 実際に外から使われるこのコンポーネントが public な関数
    このコンポーネントをテストすることで内部で使れているコードがテストできるはず💡

    View Slide

  11. © ZOZO, Inc.
    11
    コンポーネントを通してテストを書く
    例えば以下のようなコンポーネント
    - Articles
    - index.ts // Articlesをexport
    - Articles.tsx
    - CategorySelect.tsx
    - ArticleList.tsx
    - Article.tsx
    - useArticles.ts
    - convertDate.ts
    ● 記事の一覧表示のコンポーネント
    ○ SelectBox があり、カテゴリを選択すると記事の一覧を取得する
    ○ useArticles は記事の取得などの処理が行われる
    ○ convertDate は投稿日時を表示用の形式に変換する処理が記述されている

    View Slide

  12. © ZOZO, Inc.
    12
    コンポーネントを通してテストを書く
    ● export されているのは Articles コンポーネント => Articles.test.tsx のテストをかく
    ○ これが public な関数
    ● Articles コンポーネントをテストすることで useArticles や convertDate の挙動も確認できる
    ○ => useArticles.test.ts を別途書く必要はない
    test("初期表示時、reactの記事を取得するリクエストが送信されること", async () => {
    // ...
    });
    test("投稿日時が意図した形式で表示されていること", async () => {
    // ...
    });

    View Slide

  13. © ZOZO, Inc.
    13
    コンポーネントを通してテストを書くポイント
    ● 小さめの機能ごとにテストを書いて下位のコンポーネントから動作を担保するイメージ
    ○ 大きいコンポーネントは検証項目が多くなりテストが難しくなる
    ○ 小さい範囲では網羅的にテストできる
    ■ 上位のコンポーネントではそこで担保すべきテストを書くといい
    ● 下位のコンポーネントの内容を再度テストする必要はない
    ● それぞれの責任範囲を意識する
    ● 全ての状態を網羅するのが難しいとき
    ○ もしかするとそのコンポーネントは責務を持ちすぎているかもしれない
    ○ renderHook などを利用して個別にテストを書く
    ■ この場合はコンポーネントを通したテストでは代表的なケースのみにするなどバランス
    をとる

    View Slide

  14. © ZOZO, Inc.
    14
    Q: 同じようなテストを何回も書いている気がする
    A: どこで何を担保するのかを考えてテストを書くといいかも
    ● 先述の Atricles の例
    ○ もし convertDate が useArticles で使われている場合
    ■ 両方にテストを書くとかなり似てしまう
    ● プロジェクトの規約的に fetch するコードは一箇所にまとめている
    ○ fetch するコードのテストはそのディレクトリで書く
    ○ Articles でも SelectBox を変更した時にリクエストを確認するようなテストを書く
    ■ それぞれで書くテストはテストしたいことが違う
    ■ => チームでテスト方針を相談できると良さそう

    View Slide

  15. © ZOZO, Inc.
    15
    Q: コンポーネントは DOM 構造と結びついているのでテスト
    が壊れやすいのでは?
    A: そんなことはなく、むしろ a11y への意識向上に繋がります󰢏
    ● getByRole のような a11y 属性を利用して要素を取得する
    ○ 実装(コンポーネント)の詳細を意識せずににコンポーネントを扱うことができる
    ■ => DOM 構造の変更に影響を受けにくい
    ● テストで状態を検証するために WAI-ARIA などを意識することが増える
    ○ 例えば tab の切り替えを検証するために aria-selected を用いて状態を表現する
    ○ ARIA属性を利用してテスタブルにする
    ■ => a11y への意識が向上

    View Slide

  16. © ZOZO, Inc.
    カスタムフックのテスト
    16

    View Slide

  17. © ZOZO, Inc.
    17
    共通で使われるカスタムフック
    ● 各プロジェクトには共有の Hooks 置き場がある
    ○ 複数の箇所から使われるカスタムフック
    ● こういう Hook もコンポーネントを通してテストする
    ○ Hook は必ずコンポーネントで使われる
    ○ 実際に使ってみることになるので、使い心地がわかる
    ■ コンポーネントを通してテストすることで実際の使用方法の例示にもなる
    次のページでサンプルコンポーネントを用意したのでみてみましょう

    View Slide

  18. © ZOZO, Inc.
    18
    共通で使われるカスタムフック
    const Component = () => {
    const { onCategoryChange, isLoading, error, articles } = useArticles({ initialCategory: CATEGORY.react });
    const changeAngular = () => { onCategoryChange(CATEGORY.angular);};
    return (


    {isLoading && }

    {articles.map((article) => ({article.title}))}

    {error != null && }

    );
    };
    ● 動作確認に必要な最小限の機能のコンポーネント
    ○ button なども data-testid で引っ張れる様にしている(getByRole は若干遅いらしい)

    View Slide

  19. © ZOZO, Inc.
    19
    共通で使われるカスタムフック
    test("記事情報の取得中isLoadingはtrueになり、取得後はfalseになる", async () => {
    const articles = generateMockArticles({ category: "react", length: 3 });
    server.use(
    rest.get(buildEndpoint("react"), (req, res, ctx) => {
    return res(ctx.json(articles));
    })
    );
    render();
    expect(screen.getByTestId("loading")).toBeInTheDocument();
    expect(await screen.findByTestId("loading")).not.toBeInTheDocument();
    expect(screen.getAllByTestId("article").length).toBe(3);
    });
    ● getByTestId メインで利用して要素を掴んでいる
    ● renderHook だと loading 状態のテストが難しいがコンポーネントだと書くことができる

    View Slide

  20. © ZOZO, Inc.
    20
    Q: カスタムフックではない関数のテストもコンポーネント
    でテストするべき?
    A: 共通の関数などは普通に単体テストで良い
    ● コンポーネントと密接に結びついているのであればコンポーネントからテストできる
    ○ そうでないなら普通にテストするでOK
    ● 例えばどんな関数?
    ○ getServerSideProps
    ○ バリデーション関係
    ○ fetch のラッパー関数
    ○ などなど

    View Slide

  21. © ZOZO, Inc.
    まとめ
    21

    View Slide

  22. © ZOZO, Inc.
    22
    まとめ
    ● React のテストすべき public な関数はコンポーネント
    ○ export されているコンポーネントを通してテストを書く
    ○ 小さめの機能ごとにテストを書いて下位のコンポーネントから動作を担保
    ● コンポーネントを利用したテストで a11y への意識向上に繋がる
    ● どこで何を担保するのかを考えてテストを書く
    ● カスタムフックもコンポーネントを利用してテストを書く
    ○ Hook は必ずコンポーネントで使用される
    ○ 使用方法の例示になる

    View Slide

  23. © ZOZO, Inc.
    23
    色々話してきましたが、
    ● どんなテストであってもテストがまったくないよりはあった方が良い
    ○ 不要になれば捨てたら良い
    ■ プロダクションコードには影響がない
    ○ 書かないと掴めない部分もある
    ■ RSC の登場で考え方が変わる可能性もある
    ● テストは自分が書いたコードが意図通りに動いているかを確認するためのもの
    ○ 将来の変更で意図しない変更がないことを担保するためのもの
    ○ コードの仕様を把握しやすくするためのもの
    ■ 「〇〇の時はxxになる」
    最後に

    View Slide

  24. © ZOZO, Inc.
    楽しくテストを書いていきましょう🔥
    24

    View Slide

  25. View Slide