Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
React でコンポーネントを利用したテストをゴリゴリ書く
Search
bom-shibuya
November 06, 2023
Programming
3
3.8k
React でコンポーネントを利用したテストをゴリゴリ書く
2023/11/06 ZOZO Tech Meetup - Webフロントエンド
bom-shibuya
November 06, 2023
Tweet
Share
Other Decks in Programming
See All in Programming
Djangoアプリケーション 運用のリアル 〜問題発生から可視化、最適化への道〜 #pyconshizu
kashewnuts
1
260
Ça bouge du côté des animations CSS !
goetter
2
150
Ruby on cygwin 2025-02
fd0
0
180
バッチを作らなきゃとなったときに考えること
irof
2
520
プログラミング言語学習のススメ / why-do-i-learn-programming-language
yashi8484
0
160
ファインディLT_ポケモン対戦の定量的分析
fufufukakaka
0
920
Formの複雑さに立ち向かう
bmthd
1
930
Bedrock Agentsレスポンス解析によるAgentのOps
licux
3
920
ML.NETで始める機械学習
ymd65536
0
230
SwiftUI Viewの責務分離
elmetal
PRO
2
270
color-scheme: light dark; を完全に理解する
uhyo
7
490
苦しいTiDBへの移行を乗り越えて快適な運用を目指す
leveragestech
0
1k
Featured
See All Featured
Automating Front-end Workflow
addyosmani
1368
200k
How to train your dragon (web standard)
notwaldorf
91
5.9k
Fireside Chat
paigeccino
34
3.2k
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
33
2.1k
We Have a Design System, Now What?
morganepeng
51
7.4k
Docker and Python
trallard
44
3.3k
How GitHub (no longer) Works
holman
314
140k
Put a Button on it: Removing Barriers to Going Fast.
kastner
60
3.7k
Statistics for Hackers
jakevdp
797
220k
Optimizing for Happiness
mojombo
376
70k
Agile that works and the tools we love
rasmusluckow
328
21k
Typedesign – Prime Four
hannesfritz
40
2.5k
Transcript
React でコンポーネントを利用し たテストをゴリゴリ書く 株式会社ZOZO ZOZOTOWN開発1部 WEBフロントエンドブロック 渋谷 拓正 Copyright ©
ZOZO, Inc. 1
© ZOZO, Inc. 株式会社ZOZO ZOZOTOWN開発1部 WEBフロントエンドブロック 渋谷 拓正 Web 制作や
CRM サービスの Web アプリの開発に関 わった後、2023年4月に ZOZO に入社。 プロテインを朝晩摂取したり絵を描いたりしています。 子供と遊んで毎日過ごしています。 2
© ZOZO, Inc. https://zozo.jp/ 3 • ファッションEC • 1,500以上のショップ、8,900以上のブランドの取り扱い •
常時95万点以上の商品アイテム数と毎日平均2,900点以上の新着 商品を掲載(2023年6月末時点) • ブランド古着のファッションゾーン「ZOZOUSED」や コスメ専門モール「ZOZOCOSME」、靴の専門モール 「ZOZOSHOES」、ラグジュアリー&デザイナーズゾーン 「ZOZOVILLA」を展開 • 即日配送サービス • ギフトラッピングサービス • ツケ払い など
© ZOZO, Inc. 4 3 年前にコンポーネントではなく Hook 自体をテストしたいというモチベーションから「React Hooksで テストをゴリゴリ書きたい」という記事を書きました。
はじめに 記事URL: https://zenn.dev/bom_shibuya/articles/5c3ae7745c5e94
© ZOZO, Inc. 5 はじめに • 当時の考え ◦ hook やコンポーネントで使われる関数がそれぞれ正しく動いている
▪ コンポーネント内の実装はそれらを組み合わせている部分が大きい • ある程度正しいことが担保されているのでは? ◦ コンポーネントは JSX が書かれている ▪ DOM 構造と結びついているので壊れやすいのでは? • それから3年経って今どのようなことを考えているか ◦ => 今日のお話 🔥
© ZOZO, Inc. 6 今日話すこと • コンポーネントを利用したテストを書く • カスタムフックのテスト テストの具体的な書き方ではなく、どの様な考えでテストを書くかをお話しできればと思っています
🙌
© ZOZO, Inc. こんな経験はありませんか? 7
© ZOZO, Inc. 8 こんな経験はありませんか? • Hook のテストは書いたがコンポーネントにあるロジックはテストしていない • 修正のたびにテストも直す必要がある
◦ 毎回直さなければならないので、そもそもテストを書くのが面倒に感じる、、、 • 何度も同じようなテストコードを書いている気がする ◦ 別のテストファイルで同じようなテストを書いた気がする、、、
© ZOZO, Inc. コンポーネントを通してテストを書く 9
© ZOZO, Inc. 10 public なメソッドをテストする • 実装の詳細をテストしてしまっているのかも • private
なメソッドはテストを書かない ▪ private なメソッドは public なメソッドを通して必ず利用されているはず ▪ 参考: t-wada 「プライベートメソッドのテストは書かないもの?」 • React にとって public な関数とは? ◦ => コンポーネント ◦ ある機能を満たす形でディレクトリが切られそこから 1 つコンポーネントが export される ▪ 実際に外から使われるこのコンポーネントが public な関数 このコンポーネントをテストすることで内部で使れているコードがテストできるはず💡
© ZOZO, Inc. 11 コンポーネントを通してテストを書く 例えば以下のようなコンポーネント - Articles - index.ts
// Articlesをexport - Articles.tsx - CategorySelect.tsx - ArticleList.tsx - Article.tsx - useArticles.ts - convertDate.ts • 記事の一覧表示のコンポーネント ◦ SelectBox があり、カテゴリを選択すると記事の一覧を取得する ◦ useArticles は記事の取得などの処理が行われる ◦ convertDate は投稿日時を表示用の形式に変換する処理が記述されている
© ZOZO, Inc. 12 コンポーネントを通してテストを書く • export されているのは Articles コンポーネント
=> Articles.test.tsx のテストをかく ◦ これが public な関数 • Articles コンポーネントをテストすることで useArticles や convertDate の挙動も確認できる ◦ => useArticles.test.ts を別途書く必要はない test("初期表示時、reactの記事を取得するリクエストが送信されること", async () => { // ... }); test("投稿日時が意図した形式で表示されていること", async () => { // ... });
© ZOZO, Inc. 13 コンポーネントを通してテストを書くポイント • 小さめの機能ごとにテストを書いて下位のコンポーネントから動作を担保するイメージ ◦ 大きいコンポーネントは検証項目が多くなりテストが難しくなる ◦
小さい範囲では網羅的にテストできる ▪ 上位のコンポーネントではそこで担保すべきテストを書くといい • 下位のコンポーネントの内容を再度テストする必要はない • それぞれの責任範囲を意識する • 全ての状態を網羅するのが難しいとき ◦ もしかするとそのコンポーネントは責務を持ちすぎているかもしれない ◦ renderHook などを利用して個別にテストを書く ▪ この場合はコンポーネントを通したテストでは代表的なケースのみにするなどバランス をとる
© ZOZO, Inc. 14 Q: 同じようなテストを何回も書いている気がする A: どこで何を担保するのかを考えてテストを書くといいかも • 先述の
Atricles の例 ◦ もし convertDate が useArticles で使われている場合 ▪ 両方にテストを書くとかなり似てしまう • プロジェクトの規約的に fetch するコードは一箇所にまとめている ◦ fetch するコードのテストはそのディレクトリで書く ◦ Articles でも SelectBox を変更した時にリクエストを確認するようなテストを書く ▪ それぞれで書くテストはテストしたいことが違う ▪ => チームでテスト方針を相談できると良さそう
© ZOZO, Inc. 15 Q: コンポーネントは DOM 構造と結びついているのでテスト が壊れやすいのでは? A:
そんなことはなく、むしろ a11y への意識向上に繋がります • getByRole のような a11y 属性を利用して要素を取得する ◦ 実装(コンポーネント)の詳細を意識せずににコンポーネントを扱うことができる ▪ => DOM 構造の変更に影響を受けにくい • テストで状態を検証するために WAI-ARIA などを意識することが増える ◦ 例えば tab の切り替えを検証するために aria-selected を用いて状態を表現する ◦ ARIA属性を利用してテスタブルにする ▪ => a11y への意識が向上
© ZOZO, Inc. カスタムフックのテスト 16
© ZOZO, Inc. 17 共通で使われるカスタムフック • 各プロジェクトには共有の Hooks 置き場がある ◦
複数の箇所から使われるカスタムフック • こういう Hook もコンポーネントを通してテストする ◦ Hook は必ずコンポーネントで使われる ◦ 実際に使ってみることになるので、使い心地がわかる ▪ コンポーネントを通してテストすることで実際の使用方法の例示にもなる 次のページでサンプルコンポーネントを用意したのでみてみましょう
© ZOZO, Inc. 18 共通で使われるカスタムフック const Component = () =>
{ const { onCategoryChange, isLoading, error, articles } = useArticles({ initialCategory: CATEGORY.react }); const changeAngular = () => { onCategoryChange(CATEGORY.angular);}; return ( <div> <button type="button" data-testid="changeAngular" onClick={changeAngular} /> {isLoading && <div data-testid="loading" />} <ul> {articles.map((article) => (<li data-testid="article" key={article.id}>{article.title}</li>))} </ul> {error != null && <div data-testid="error" />} </div> ); }; • 動作確認に必要な最小限の機能のコンポーネント ◦ button なども data-testid で引っ張れる様にしている(getByRole は若干遅いらしい)
© 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(<Component />); expect(screen.getByTestId("loading")).toBeInTheDocument(); expect(await screen.findByTestId("loading")).not.toBeInTheDocument(); expect(screen.getAllByTestId("article").length).toBe(3); }); • getByTestId メインで利用して要素を掴んでいる • renderHook だと loading 状態のテストが難しいがコンポーネントだと書くことができる
© ZOZO, Inc. 20 Q: カスタムフックではない関数のテストもコンポーネント でテストするべき? A: 共通の関数などは普通に単体テストで良い •
コンポーネントと密接に結びついているのであればコンポーネントからテストできる ◦ そうでないなら普通にテストするでOK • 例えばどんな関数? ◦ getServerSideProps ◦ バリデーション関係 ◦ fetch のラッパー関数 ◦ などなど
© ZOZO, Inc. まとめ 21
© ZOZO, Inc. 22 まとめ • React のテストすべき public な関数はコンポーネント
◦ export されているコンポーネントを通してテストを書く ◦ 小さめの機能ごとにテストを書いて下位のコンポーネントから動作を担保 • コンポーネントを利用したテストで a11y への意識向上に繋がる • どこで何を担保するのかを考えてテストを書く • カスタムフックもコンポーネントを利用してテストを書く ◦ Hook は必ずコンポーネントで使用される ◦ 使用方法の例示になる
© ZOZO, Inc. 23 色々話してきましたが、 • どんなテストであってもテストがまったくないよりはあった方が良い ◦ 不要になれば捨てたら良い ▪
プロダクションコードには影響がない ◦ 書かないと掴めない部分もある ▪ RSC の登場で考え方が変わる可能性もある • テストは自分が書いたコードが意図通りに動いているかを確認するためのもの ◦ 将来の変更で意図しない変更がないことを担保するためのもの ◦ コードの仕様を把握しやすくするためのもの ▪ 「〇〇の時はxxになる」 最後に
© ZOZO, Inc. 楽しくテストを書いていきましょう🔥 24
None