本記事は筆者の考えをベースに、ChatGPT 5.5 を用いて構成・推敲したものです。
内容の主張と判断は筆者によるものであり、文章表現の整理に ChatGPT 5.5 を活用しています。
はじめに:神格化されるテスト
現代のソフトウェア開発では、テストはほとんど「正しい開発者であるための条件」のように語られることがあります。
技術記事、社内規約、大規模開発のベストプラクティスなどを見ていると、よく次のような言葉に出会います。
テストが通らないコードはリリースしてはいけない
テストのないコードは信用できない
プロジェクト開始時点から高いカバレッジを目指すべきだ
もちろん、これらの考え方には十分な理由があります。
金融、医療、決済、インフラ、組み込み制御など、失敗のコストが非常に高い領域では、テストは品質を支える重要な基盤です。
しかし一方で、すべてのプロジェクト、すべてのフェーズ、すべてのチームに対して、同じ温度感でテストを要求するのは本当に正しいのでしょうか。
私は、数千行のストアドプロシージャから、数万行規模の業務ロジック、短期間で仕様が変わるプロダクト開発まで経験する中で、次のような疑問を持つようになりました。
テストは本当に、常に早ければ早いほどよいのでしょうか。
カバレッジは本当に、高ければ高いほどよいのでしょうか。
もっと直接的に言えば、テストはある種の「政治的正しさ」として扱われすぎていないでしょうか。
この記事は、テスト不要論ではありません。
私が問題にしたいのは、テストそのものではなく、プロジェクトの状況を見ずにテストを絶対視する姿勢です。
テストには見えにくいコストがある
1. 変化の激しい初期開発では、テストが「短命な資産」になる
プロジェクト初期のビジネスロジックは、とにかく不安定です。
今週追加した機能が、来週には市場の反応を見て削除されることがあります。
昨日まで正しかった画面遷移が、次の打ち合わせで丸ごと変わることもあります。
このような状況で、まだ安定していない一時的なロジックに対して大量の自動テストを書くことは、ある意味では「すぐ消える可能性が高いコードのために、もう一つの保守対象を作る」ことでもあります。
コードが変われば、テストも変える必要があります。
つまりチームは、機能コードの変更コストだけでなく、テストコードの同期コストも同時に背負うことになります。
もちろん、テストによって仕様変更の影響を検出できるというメリットはあります。
しかし、そもそも仕様自体がまだ仮説にすぎない段階では、そのテストの寿命が非常に短くなる可能性があります。
この段階で考えるべきなのは、
「テストを書くべきかどうか」ではなく、「このテストは保守する価値があるほど長生きするのか」
という問いだと思います。
2. 優秀なリソースがテスト保守に吸われる
価値のあるテストを書くのは簡単ではありません。
単にメソッドを呼び出して戻り値を Assert するだけなら誰でもできます。
しかし、本当に意味のあるテストを書くには、業務ルール、境界条件、例外パターン、状態遷移、外部依存などを理解している必要があります。
つまり、高品質なテストを書く人は、機能を実装する人と同じか、それ以上に対象ドメインを理解している必要があります。
ここで現実的な問題が出てきます。
リソースが限られたチームでは、最も業務を理解している人、最も設計を理解している人は、同時に最もコア機能を前に進められる人でもあります。
その人たちの時間が、まだ安定していないテストケースの作成や修正に大きく吸われると、プロダクトそのものの前進速度が落ちる可能性があります。
特に、プロダクトがまだ生き残れるかどうか分からない段階では、これはかなり危険です。
まだ市場で生存確認ができていないプロダクトが、完璧なテスト体制を目指して開発速度を失う。
これは、きれいに見えて、実はかなり高コストな判断かもしれません。
3. テストコードもまたバグる
テストコードもコードです。
コードである以上、バグがあります。
誤った前提があります。
過剰なモックがあります。
保守コストがあります。
実際の現場では、次のようなことがよく起こります。
- テストケース自体が間違っている
- モックの振る舞いが現実の実装とズレている
- ビジネス結果ではなく実装詳細をテストしている
- 本体コードは問題ないのに、脆いテストが頻繁に落ちる
- リファクタリングを助けるはずのテストが、逆にリファクタリングの障害になる
もちろん、これはテストが無意味だという話ではありません。
むしろ逆です。
低品質なテストは、品質保証ではなく技術的負債になります。
「テストがある」という事実だけでは、品質は保証されません。
価値のあるテストが、適切な粒度で、適切な対象に対して書かれていることが重要です。
4. カバレッジは品質そのものではない
コードカバレッジは便利な指標です。
どのコードがテスト実行時に通過したかを可視化できます。
テストの抜け漏れを見つける手がかりにもなります。
しかし、カバレッジはあくまで観測指標であって、品質そのものではありません。
カバレッジが KPI になると、開発者は数字を上げるために低価値なテストを書き始めます。
たとえば、
- 単純な Getter / Setter のテスト
- 業務的な意味のないコンストラクタテスト
- カバレッジのためだけに作った不自然な分岐テスト
- 実行されたことだけを確認し、正しさを確認しないテスト
こうしたテストによって、レポート上の数字は良くなります。
しかし、それでシステムが本当に堅牢になったかどうかは別問題です。
カバレッジは参考になります。
しかし、カバレッジは品質そのものではありません。
実用主義としてのテスト戦略
私はテストに反対しているわけではありません。
反対しているのは、プロジェクトのフェーズ、リスク、チームの状況を無視した「テスト絶対主義」です。
テストは信仰ではなく、道具です。
道具である以上、使うタイミング、使う対象、使うコストを考える必要があります。
フェーズ A:初期開発では Fail Fast を優先する
ビジネスの方向性がまだ明確でなく、仕様が頻繁に変わる段階では、最も重要なのは完璧なテスト体系を作ることではありません。
この段階で重要なのは、次のようなことです。
- コア機能を素早く形にする
- 仮説を早く検証する
- 間違った方向に進んでいないか早く気づく
- 致命的なロジックの破綻を開発中にすぐ発見する
つまり、初期開発では「網羅的な安全網」よりも「高速なフィードバック」の方が重要になることがあります。
たとえば .NET であれば、開発中に Debug.Assert を積極的に使うことで、想定外の状態を早期に検出できます。
Debug.Assert(order.TotalAmount >= 0);
Debug.Assert(userId != Guid.Empty);
また、絶対に起きてはいけない状態に対しては、開発環境で即座に止めるという選択肢もあります。
if (state is null)
{
Debugger.Break();
throw new InvalidOperationException("Unexpected null state.");
}
ここで重要なのは、Debugger.Break() や Debug.Assert() がテストの代替になる、という意味ではありません。
それらはテストとは役割が違います。
これらの目的は、開発中に不正な状態をできるだけ早く爆発させることです。
静かに壊れるより、早く壊れる。
原因が遠くに流れてから気づくより、その場で止まる。
初期開発では、このような Fail Fast の仕組みが非常に有効なことがあります。
フェーズ B:業務が安定してからテストを補強する
ビジネスロジックが市場や運用を通じて検証され、システムの中に「守る価値のあるルール」が見えてきた段階では、テストの価値は大きく上がります。
この段階になると、次のようなものが明確になってきます。
- どの機能が本当に使われているか
- どの業務ルールが安定しているか
- どのモジュールが壊れやすいか
- どの処理が手動確認しづらいか
- どのバグが再発すると致命的か
つまり、テストを書くべき対象が見えてきます。
このタイミングで書くテストは、初期段階で仮説に対して書くテストよりも、はるかに費用対効果が高くなります。
特に優先すべきなのは、次のような領域です。
- コア業務フロー
- 複雑な状態遷移
- 過去にバグが発生した箇所
- 外部 API との境界
- データ整合性に関わる処理
- 仕様変更の影響を受けやすい処理
この段階でのテストは、「テストを書いている感」を出すためのものではありません。
本当に壊れてほしくない部分を守るための安全網です。
言い換えると、
テストは行数をカバーするためではなく、リスクをカバーするために書くべきです。
テストは工程を支配するものではなく、工程に仕えるもの
ソフトウェア開発は、単一の指標で評価できるものではありません。
カバレッジが高ければ高品質とは限りません。
テストが多ければ安全とは限りません。
テストがないコードが、必ずしも無責任とも限りません。
本当に考えるべきなのは、次のような問いです。
- 今のプロジェクトはどの段階にあるのか
- 今の最大リスクは何か
- チームで最も不足しているリソースは何か
- このテストはどれくらい長く保守されるのか
- このテストは実際にどのリスクを下げるのか
- テストの保守コストは、その効果に見合っているのか
これらを考えずに「テストは必須」と言うだけでは、工程判断としては不十分です。
テストは大事です。
しかし、テストが大事だからこそ、雑に神格化してはいけないと思います。
おわりに:テストは信仰ではなく、道具である
ソフトウェア工学に銀の弾丸はありません。
テストも例外ではありません。
テストの価値は、それが「正しそうに見えること」ではなく、適切なタイミングで、適切な対象に対して、実際のリスクを下げることにあります。
避けるべき極端は二つあります。
一つは、まったくテストを書かず、品質問題をすべてユーザーや本番環境に押し付けること。
もう一つは、プロジェクトの段階やビジネス上の現実を無視して、テストを神聖視し、カバレッジやプロセスだけを追いかけることです。
成熟した工程判断は、その中間にあります。
初期開発では、まず方向性を検証する。
不正な状態は早く爆発させる。
業務が安定してきたら、壊れてほしくない部分に対してテストを追加する。
そして、テストを「数字」ではなく「リスク低減」のために使う。
テストは、チームが正しさを演出するためのものではありません。
テストは、システムの本当に重要な部分を守るための道具です。
最終的にプロダクトの生命力を決めるのは、100% のカバレッジレポートではありません。
それがユーザーの問題を解決しているかどうかです。