Slide 1

Slide 1 text

React でコンポーネントを利用し たテストをゴリゴリ書く 株式会社ZOZO
 ZOZOTOWN開発1部 WEBフロントエンドブロック
 渋谷 拓正 Copyright © ZOZO, Inc. 1

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

© 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 は若干遅いらしい)

Slide 19

Slide 19 text

© 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 状態のテストが難しいがコンポーネントだと書くことができる

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

© ZOZO, Inc. まとめ 21

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

No content