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.4k
React でコンポーネントを利用したテストをゴリゴリ書く
2023/11/06 ZOZO Tech Meetup - Webフロントエンド
bom-shibuya
November 06, 2023
Tweet
Share
Other Decks in Programming
See All in Programming
Boost Performance and Developer Productivity with Jakarta EE 11
ivargrimstad
0
840
Progressive Web Apps für Desktop und Mobile mit Angular (Hands-on)
christianliebel
PRO
0
110
CSC509 Lecture 08
javiergs
PRO
0
110
詳細解説! ArrayListの仕組みと実装
yujisoftware
0
480
Sidekiqで実現する 長時間非同期処理の中断と再開 / Pausing and Resuming Long-Running Asynchronous Jobs with Sidekiq
hypermkt
6
2.7k
go.mod、DockerfileやCI設定に分散しがちなGoのバージョンをまとめて管理する / Go Connect #3
arthur1
10
2.4k
OpenTelemetryでRailsのパフォーマンス分析を始めてみよう(KoR2024)
ymtdzzz
4
1.6k
開発効率向上のためのリファクタリングの一歩目の選択肢 ~コード分割~ / JJUG CCC 2024 Fall
ryounasso
0
360
ECS Service Connectのこれまでのアップデートと今後のRoadmapを見てみる
tkikuc
2
210
Vitest Browser Mode への期待 / Vitest Browser Mode
odanado
PRO
2
1.7k
Importmapを使ったJavaScriptの 読み込みとブラウザアドオンの影響
swamp09
4
1.2k
『ドメイン駆動設計をはじめよう』のモデリングアプローチ
masuda220
PRO
8
440
Featured
See All Featured
The Power of CSS Pseudo Elements
geoffreycrofte
72
5.3k
Side Projects
sachag
452
42k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
159
15k
YesSQL, Process and Tooling at Scale
rocio
167
14k
What’s in a name? Adding method to the madness
productmarketing
PRO
22
3.1k
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
167
49k
How to Think Like a Performance Engineer
csswizardry
19
1.1k
How to train your dragon (web standard)
notwaldorf
88
5.7k
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
41
2.1k
Agile that works and the tools we love
rasmusluckow
327
21k
Principles of Awesome APIs and How to Build Them.
keavy
126
17k
Speed Design
sergeychernyshev
24
570
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