next.js でカバレッジを取る


next.js の unit テストと E2E テストでカバレッジを取る方法について調査しました。

SWC か babel か

next.js は2つのビルド方法があります。最近追加された Rust ベースの高速なコンパイラ(SWC)を使う方法と、旧来の方式である babel を使う方法です。今後は SWC に移行していくようなので、 SWC を使用することにしました。

テストの種類

next.js では主に2種類のテスト方法があります。1つはユニットテスト、もう一つは E2E テストです。ユニットテストは next.js の各ページを単純に React のコンポーネントとしてテストします。今回は jest を使用します。E2E テストは next.js サーバーを起動して、ブラウザ経由でテストを行います。こちらは Playwright を使用することにしました。

ユニットテストは next.js のドキュメントを参考に導入できます。カバレッジの設定は jest の機能を利用します。

E2E テストは少し考える必要があります。 next.js の特徴として、サーバー側でレンダリングできるものはサーバー側で処理してからブラウザへ返却します。また、 クライアント側の操作による変更はクライアント側(ブラウザ側)で描画することもあります。要するにサーバー側とクライアント側、それぞれでカバレッジを取得する必要があります。

E2E のサーバーサイドのテストでは、 next.js のサーバーにカバレッジを出力する処理を注入する必要があります。babel の場合は babel-plugin-istanbul を使うことが多いようですが、 SWC では swc-plugin-coverage-instrument を使用します1この辺から情報を得ました。

E2E のクライアントサイドのテストは、 Playwright のドキュメントを参考に実装します。ブラウザの機能でカバレッジを取得します。正確には V8 の機能のようなので chromium のみ利用可能です。

まとめると、下記の3種類の方法でカバレッジを取得することができます。

  • unit testing: jest
  • E2E server side: swc-plugin-coverage-instrument
  • E2E client side: Playwright

ソースとテスト

少々複雑になったので、 GitHub にソースを上げました。実装自体はかなり単純で、 getServerSideProps と getStaticProps のページをそれぞれ1つづつ用意した程度です。条件分岐のカバレッジも見たかったので、それぞれのページにクエリパラメーターによって画面表示を変える処理を入れています。

テストのセットアップは jest は簡単なのですが、 E2E のクライアントサイドの実装が少々面倒でした。特に fixtures.ts に書いた内容です。カバレッジのソースパスが URL や webpack-internal:// になっているので、それをローカルのファイルパスに変換する必要があります。

動かしてみる

jest

jest のカバレッジはきれいに出ています。これならばカバレッジ 100% を維持できるかもしれません(カバレッジを維持するためのテストに意味があるかはおいといて)。各ページのプロパティの取得部分 (getServerSideProps や getStaticProps) と React 部分はそれぞれ別でテストすることになるので、細かい粒度での「単体」テストにはなってしまいます。

E2E server-side

E2E のサーバー側のテストはやはりプロパティの値をそのまま表示するようなページのカバレッジはそこそこ取得可能です。クライアント側の処理が増えていくとカバレッジは減っていくことになります。

E2E client-side

E2E のクライアント側のカバレッジは不安定な感じがします。ブランチ(関数や分岐)のカバレッジが、全パターン通しているにもかかわらず不足している箇所があるためです。TypeScript のマッピングがうまくいっていないのか、バグなのか、原因を特定するのは難しそうです。

今回、 E2E のサーバーとクライアントは別々にビルド&テスト実行するという非効率な方法をとりました。両方同時にカバレッジを取ると、クライアント側のカバレッジが下がる事象があったためです。サーバー側に注入したカバレッジ用のコードが影響しているのかもしれません。それぞれ別で取得すると安定します。

マージした結果

これら3つのカバレッジを nyc ですべてマージした結果です。ユニットテストのカバレッジに寄って 100% になればよかったのですが、低い方の影響を受けてしまうようです。node.js 側 (jest と E2E のサーバー側)と V8 (ブラウザ側) でソース構造の評価方法が違うようなので、E2E は NG だったけどユニットテストで通ったから OK とはならないようです。

codecov

ただ、3つのカバレッジリポートを codecov にアップロードするとカバレッジが 100% になりました。マージの方法はツールによって異なるようです。

まとめ

テストというのはカバレッジ 100% を目指すことではないのですが、カバレッジを見ることでテストの漏れは発見することができます。ユニットテストでは意味のないテスト(最近あったのはファイル選択ダイアログのテスト。jest ではダイアログを再現できない)は E2E でテストすることで網羅する、といった体制を組みたかったためこの調査をしました。両方のカバレッジをマージしてテスト漏れを防ぎたかったのです。ただ、上記の結果を見ると若干の不安定さが気になります。

実際にもう少し使ってみて、カバレッジの精度を見極めようと思います。それと並行して、このコードは UT で、このコードは E2E で見てるという情報が分かりやすくなる方法が他にないか探してみようと思います。


コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください