React / Reduxの推奨するテストの書き方の変遷まとめ
ここ数年の、React / Redux でユニットテストよりインテグレーションテストを重視するようになった背景についての日本語の情報が少なかったので書きます。
この記事の内容は、Redux公式ドキュメントの内容とそのコミットログ、またReactコミュニティ Reactiflux のDiscordでの会話などを参考に執筆したものです。
主に参考にしたページ・ディスカッション:
- Redux公式ドキュメント
Writing Tests | Redux - 2021年6月のテストについてのドキュメント更新のプルリク
📝 Update writing tests page by Shrugsy · Pull Request #4116 · reduxjs/redux · GitHub - Reactiflux Discordサーバ の #testing channelでの議論
Reactiflux
TL; DR:
- Redux のドキュメントでは、2021年中ごろまでは Redux のロジックや selector を React コンポーネントから分離して 単体でテストすることを公式ドキュメントで標準とされていた。*1
- 2021年7月以降は、React コンポーネントと可能な限り一体で(「インテグレーション」で)テストすることを推奨している。*2
時期ごとのまとめ
2018年(React Testimg Library(以下、RTL)は2018年3月リリース)ごろまで
基本的には、コンポーネントは Shallow Rendering でテストし、Redux や メソッド*4 などのロジックは
Discordさかのぼった感じ、RTLリリース前の時点ではインテグレーションテストは「コストが高く見合わない」という意見が多かったみたいです。*5
また、インテグレーションテストを実装することでユニットテストが不要になるというアイデアに対しては、概して否定的なコメントが寄せられています。*6
RTLリリース後(2018年後半~)
React Testing Library(RTL)は、Reactのインテグレーションテストを実装するためのライブラリです。2018年3月にリリースされました。
RTLにより、インテグレーションテストの実装コストが大きく下がりました。
これにより、Kent C. Doddsさんの提唱する Testing Trophy のインテグレーションテスト・ユニットテスト比を実現しやすくなりました。
Redux では、2021年までには*7 ユーザが実際に利用するコンポーネントのUIこそがテスト対象であり、それ以外の Redux (や Hooks) はすべて実装の詳細として扱うのを原則とすべき、と公式ドキュメントでも明記されるようになりました。*8
- 「コンポーネントを介さない、純粋なユニットテストを書きたい場合は容易に書くことができます。ただし、本当にそれが必要かを考えてから書いてください。」ドキュメントでは念押しされています。*9
- RTLのメンテナ達の間では、インテグレーションテストで確かめられる内容をユニットテストで実装することは悪だと考えている人もいます*10 が、Kent C. Dodds さんは必ずしもそういうわけではない*11 みたいです。
2022年時点でのReduxのテスト実装方針
Reduxコミュニティがインテグレーションテストをメインで実装することを推奨しているわけなので、原則インテグレーションテストのみを実装することにすればよいかと思います。
ユニットテストは、複雑で、インテグレーションテストでカバーするのが難しい部分においてのみ適用するのが良いでしょう。
実際のところ、ここ数年のRedux Toolkit (以下、RTK) と RTK Query の発展も、ユニットテストの省略を後押ししてくれているのではないかと思います。
従来フロントエンドのロジックといえば、バックエンドAPIと通信しデータを適切にキャッシュすることでした。
RTK と RTK Query がこのキャッシュ管理のロジックのかなりの部分を吸収してくれるようになった現代フロントエンドでは、ロジックの大部分は自前で実装するものではなく、RTK・RTK Query をビジネス要件に合う形で適切に呼び出すものへと変化してきているように感じられます。
このように薄くなったフロントエンドのロジックに対してユニットテストを書く意味はあまりなく*12、インテグレーションテストとしてコンポーネントを介してビジネス要件を満たすことができているかを確かめることに時間を使うほうがかなり有意義だと私は考えています。
*1:https://github.com/reduxjs/redux/pull/4116
*2:https://redux.js.org/usage/writing-tests
*3:https://redux.js.org/usage/writing-tests#guiding-principles
*4:当時はClass Component全盛期で、Function Componentが使われるようになるには 2019年2月のHooksの実装 を待つ必要があった。
*5:https://discord.com/channels/102860784329052160/105703173393485824/423150735354494986
*6:https://discord.com/channels/102860784329052160/105703173393485824/423143978536402944 ここでの会話の結論: ユニットテストとインテグレーションテストの確認するポイントが異なっており、どちらもあるに越したことはない。
*7:https://github.com/reduxjs/redux/pull/4116
*8:https://redux.js.org/usage/writing-tests
*9:https://redux.js.org/usage/writing-tests#guiding-principles
*10: このIssueのプルリクでは、hookのユニットテストを書くこと自体がやり玉に挙げられている。結局使えなくなった。
*11:どっちでもよさそう です。How to test custom React hooks にあるように、色々考慮した結果ユニットテストにしよう、ってのは全然ありだと思っていそうです。
*12:ビジネス要件に対して「適切に」呼び出していることを確かめたいのに、ユニットテストでは内部実装に対して「適切に」呼び出していることを確認することしかできません。