🐈

Testing Trophy を実プロジェクトで考えてみた

に公開

はじめに

「テストって書いた方がいいよ」と言われるけど、何をどこまで書けばいいのか分からない。カバレッジ100%を目指すべき? 全部の関数にテストを書くべき?

そんな疑問を持っていた自分が、業務で関わっているフロントエンドプロジェクトのテスト戦略ドキュメントを読んで、テストに対する考え方が大きく変わりました。この記事では、その学びを整理していきます。

Testing Trophy とは

Kent C. Dodds という方が提唱した、テストの考え方です。トロフィーの形をしています。

4つの層(下から順に)

🏆 Static(静的解析)
TypeScript や ESLint のことです。コードを書いた瞬間にミスを教えてくれます。一番ラクな層。

🏆 Unit(単体テスト)
関数1つだけをテストします。小さくて速いです。

🏆 Integration(統合テスト)
複数の部品を組み合わせて動かすテストです。例:「フォームに入力 → ボタン押す → 結果が表示される」みたいな流れをまとめて確認します。

🏆 E2E(エンドツーエンド)
本物のブラウザで実際にユーザーと同じ操作をします。一番リアルですが、遅くて壊れやすいです。

有名な言葉

"The more your tests resemble the way your software is used, the more confidence they can give you."
(テストが実際の使われ方に近いほど、安心感が大きい)

これが Kent さんの代表的な考え方です。だから本来は Integration を主力にしよう というのが原則とされています。

でも、現場では Unit が主力だった

ここで疑問が湧きました。「Integration が主力」なのに、Testing Trophyを採用しているプロジェクトのテスト戦略では、データ変換ロジック(以下 transforms と呼びます)の Unit テストが主力になっていることがあります。これは Trophy の思想に反しているのでは?

調べてみると、答えはこうでした。

Testing Trophy の本質は「Integration を絶対主力にしろ」ではありません。本当に大事なのは、

  • ✅ バグとリスクに合わせてテスト配分を決める
  • ✅ ユーザーの使い方に近いテストを優先的に検討する
  • ✅ 静的解析を土台にする

つまり Trophy は「形が固定された絶対ルール」ではなく、バランスのガイドラインだったのです。

原則: Integration を主力に

本質: バグとリスクに応じて配分を決める

現場: バグが特定のロジック層に集中している

結論: 本質に従って Unit が主力になる

「Integration が主力」を捨てたわけではなく、本質に従った結果、たまたま Unit が主力になったということでした。

バグを分析するという発想

ここからが、自分にとって一番の学びでした。

「どのテストを厚くするか」を決めるためには、まずどんなバグが多いのかを分析する必要があります。

直近のバグ Issue がある場合、例えば以下の観点で分析することができます。

  • 一番多いバグの種類は?
  • 一番多いバグのドメインは?
  • 一番テストが薄いところは?

結果、バグの過半数が特定のレイヤー(データ変換処理)に集中していることがわかる、などの発見ができます。

「テスト書こう」と言われるとすぐ「どう書こう?」に飛びがちですが、本来は、

  1. バグを分析する(現状把握)
  2. どこに集中するか決める(戦略)
  3. テストを書く(実装)

この順番で考えるのが正しいのだと気づきました。
「分析 → 戦略 → 実装」 のプロセスってことですね。

書くべきテスト、書かなくても良いテスト

判断基準はシンプルでした。

人間が目視で気づけないバグだけテストを書く

テストを書くもの

  • データ変換ロジック(API ⇔ フォーム変換など)
  • バリデーションスキーマ
  • 日付・数値フォーマット変換
  • ビジネスロジック(計算・判定)
  • セキュリティ関連処理

共通点は、目視では検出困難で、表面化まで時間がかかり、データ破損リスクがあることです。

テストを書かないもの

  • クリック挙動・アニメーション
  • 表示の見た目・誤植・1pxずれ
  • 単体 UI コンポーネントの描画確認
  • E2E 全般(DOM 変更で壊れやすい)

共通点は、人間が即座に発見できて、修正が容易なことです。

ボタンの色がズレているのは目で見てすぐ分かります。でもデータ変換でフィールドが1つ抜けているのは、数週間後に DB が壊れて発覚するかもしれません。テストでしか守れないものにだけテストを書くという考え方です。

テスト自体が正しいかを機械的に検証する

ここはちょっと上級の話です。

普通のテストは「実装が正しいか」を確認します。でも、それだけだとテスト自体が古かったり漏れていたら、バグを見逃してしまいます

そこで、こんな「メタテスト」を組み込むという発想があります。

メタテスト 何を基準にする 何の漏れを防ぐ
API スキーマ突合 API の型定義(自動生成) 実装のフィールド漏れ
バリデーション定義突合 Zod スキーマ テストデータの漏れ

例えるなら、

  • 普通のテスト = 「答え合わせ」
  • メタテスト = 「問題用紙に問題が全部載っているかの確認」

問題用紙に問題が抜けていたら、いくら答え合わせをしても満点に見えてしまいます。それを防ぐ仕組みです。

バグ修正の正しい順番

バグを修正するときは、先にテストを書くのがポイントです。

バグ発見

① 再現テストを書く

② テストが落ちることを確認(RED)

③ 修正する

④ テストが通ることを確認(GREEN)

ポイントは ② の「RED を確認する」ことです。先に修正してからテストを書くと、「そのテスト、本当にバグを検出できていた?」が分からなくなります。一度 RED を見ることで、テストが効いている証拠になります。

これでテストと修正をセットで PR に出すと、同じバグが二度と再発しなくなります。

まとめ:3つの大事な考え方

学んだことを3つに絞ると、こうなります。

1. テストは「保険」。全部にかける必要はなく、壊れたら困るものにだけかける

バグが集中する場所を分析で特定して、そこに集中投資します。カバレッジから逆算するのは間違いです。

2. テストと静的解析(ESLint)は役割が違う

構文・スタイルは機械に任せます。テストは「ロジックの正しさ」に集中します。||?? に直すような話はテストの守備範囲ではありません。

3. バグ修正は「テスト → RED → 修正 → GREEN」の順

これだけ守れば、同じバグが二度と再発しなくなります。

おわりに

「テストを書く」というのは、ただ作業として書くものではなく、どこにバグが集中しているかを分析して、戦略的にテストを配置するという設計の仕事でした。

Testing Trophy の本質は「Integration が主力」という形式ではなく、「リスクとバグの集中度に応じてテスト配分を決める」という思想にあることが分かりました。

次は実際に自分のプロジェクトでデータ変換ロジックのテストを1本書いてみるところから始めようと思います。

Discussion