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.6k
React でコンポーネントを利用したテストをゴリゴリ書く
2023/11/06 ZOZO Tech Meetup - Webフロントエンド
bom-shibuya
November 06, 2023
Tweet
Share
Other Decks in Programming
See All in Programming
KMP와 kotlinx.rpc로 서버와 클라이언트 동기화
kwakeuijin
0
140
Fibonacci Function Gallery - Part 1
philipschwarz
PRO
0
220
【re:Growth 2024】 Aurora DSQL をちゃんと話します!
maroon1st
0
780
開発者とQAの越境で自動テストが増える開発プロセスを実現する
92thunder
1
190
暇に任せてProxmoxコンソール 作ってみました
karugamo
2
720
各クラウドサービスにおける.NETの対応と見解
ymd65536
0
100
快速入門可觀測性
blueswen
0
370
Effective Signals in Angular 19+: Rules and Helpers
manfredsteyer
PRO
0
110
Beyond ORM
77web
7
880
nekko cloudにおけるProxmox VE利用事例
irumaru
3
430
Spatial Rendering for Apple Vision Pro
warrenm
0
110
Symfony Mapper Component
soyuka
2
730
Featured
See All Featured
Producing Creativity
orderedlist
PRO
341
39k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
44
9.3k
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
29
2k
XXLCSS - How to scale CSS and keep your sanity
sugarenia
247
1.3M
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
656
59k
Designing for Performance
lara
604
68k
Building Applications with DynamoDB
mza
91
6.1k
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
17
2.3k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
47
5.1k
Adopting Sorbet at Scale
ufuk
73
9.1k
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
330
21k
Measuring & Analyzing Core Web Vitals
bluesmoon
4
170
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