🍒

【技術屋の思考ログ #8】単体テストのいろは

に公開

1. はじめに

こんにちは。技術屋の思考ログ、第8回目です。
若手エンジニア向けの教育コンテンツをベースに、AI時代におけるシステム開発のあり方を考える連載の第2弾です。

前回は「実装(プログラミング)」についてお話ししました。今回は、その実装したコードの品質を担保するための最初の砦、「単体(ユニット)テスト」 について話していきます。

単体テストは、システムの各モジュールに対して、適切に実装できているかをチェックするテストです。単体テストを実施することで、テスト工程で発見されるバグを未然にキャッチすることができ、早期に対応が取れるため、手戻りを最小限に抑えられます。また、運用が始まってからの保守・改修コストが小さくなるという大きなメリットもあります。
今回は、私が現場で若手に伝えている単体テストの「基本と落とし穴」、そしてAI時代におけるテストの価値についてお伝えします。

2. 単体テストで「やってはいけないこと」

単体テストでは、テスト基盤を整備する必要があり、全てを単体テスト化しようとすると、初期開発時に膨大なテストコードを作成するため、初期コストが膨らむというデメリットもあります。そのため、現場では時として目的を見失った単体テスト・テスト自動化が行われることがあります。

私が若手に必ず伝える「やってはいけないこと」は以下の3つです。

  1. テスト基盤が整っていないのに、何でもかんでも自動化しないこと
  2. カバレッジを通すためだけのテストをしないこと
  3. テストが全て合格にならない状態でコミットしないこと

これらには、現場ならではの明確な理由があります。

  • 自動化の範囲を見極める:
    フレームワークで自動化が想定されていない場合、CIツールなどを導入するのに非常に大きな労力・時間が掛かります。テスト基盤が整っていない状況で全てを自動化しようとすると、初期コストが大幅に掛かり、逆にプロジェクトリスクになってしまいます。そのため、自分達のプロジェクトやチームに合った範囲での単体テストの導入を勧めるようにしています。
  • カバレッジの目的化を防ぐ:
    カバレッジを上げるためだけにテストコードを書いているケースを見たことがありますが、これは本末転倒です。単体テストいえど、きちんとテスト設計を行った上でテストを実施するべきであり、カバレッジが取れていないコードがある場合は「実は不要なコードなのではないか?」を疑うことが重要になります。
  • CI(継続的インテグレーション)の意義を守る:
    単体テストのCIを回す際に、常に全てのテストケースが合格になっていないと、コード改修時に新たなミスが発生しても見落とされてしまう可能性が高いです。単体テストのCIを回す理由は「常にコードの整合性が保たれていること」であり、何かあった時にすぐ気づける状態を維持する意識が必要です。

3. テストデータの準備は「設計」である

単体テストを実施する際、「テストデータはテストを行う際に、テスターが考える」という現場によく遭遇しますが、これは非常に危険です。データは全体を把握している人(データ管理者など)がきちんと設計しなければ、網羅性が担保されず、余計な工数ばかりが掛かってしまいます。

テストデータを設計・作成する際は、以下の点に注意するよう心掛けています。

  • 投入順序の意識: データの投入順序は、システムが実際の業務で使われる順となります。DBのFK(外部キー)制約等もあるので、業務フローで生成される順にデータを投入するべきです。
  • 網羅性と最小化: ロールや種別など、システムで分岐となる要素については、網羅性をきちんと確認して設計してください。一方で、やり過ぎると永遠に終わらないので、必要最小限のデータ量を用意しましょう。

特に1つ目の「投入順序と外部キー制約」は重要です。外部キーを設定しなかったり、データ投入後にFKを設定するような現場を見たことがあります。一見問題ないように見えるのですが、本来のシステムの使われ方(業務フローの順序)でデータを投入しないと、実際の利用状態におけるデータ整合性の確認が取れず、後に不具合が発生することがあります。
様々な現場があるので一概にこれが必ず正しいとは言いませんが、今までの経験上、データ整合性を強く意識することで、質の高いプロダクト開発ができていると確信しています。この辺を疎かにしていると、システムテストで致命的な不具合が発見され、余計な手戻りが発生する可能性があり、実際に私もそのような経験をしています。

なお、データ管理者は非常に多忙なことが多いため、整合性が取れたゴールデンパターンのテストデータ準備のみをデータ管理者が行い、それ以外は開発者やテスターに委任するようなこともしばしばあります。

4. テスト自動化と「テストファースト」のリアル

現代の開発では、CI/CDツールを用いたテストの自動化が一般的になってきています。CIツールを使って単体テストを毎日実行することにより、コードが正常であるかを日々の開発の中で確認することができるようになり、手戻り防止、品質維持が図れます。しかし、単体テストにNGがあったり、日々の確認を行わないなど、正しく運用されなければ導入コストなども踏まえてマイナス効果になってしまいます。

そして、テストを語る上で避けて通れないのが 「テスト駆動開発(TDD)」 です。
TDDはテストコードを先に用意して、そのテストがOKになるようにプログラミングを行う開発手法ですが、機能実装しない状況でテストコードを用意することは非常に難しく、非効率になるケースが多いため、私は若手に「厳密なTDDを無理にやる必要はない」と伝えています(状況によっては、TDDが良いケースもあるとは思っていますが⋯)。

一方で、「テストファースト」の概念は非常に重要なので、積極的に取り入れるように と強調しています。
実装していて「どうやってテストするんだろう?」と疑問に思った時は注意が必要で、コードが複雑すぎて、テスタビリティ(テスト容易性)が低くなっている可能性があります。テストしにくいコードは、バグが混入しやすくなっており、保守性も悪く、QCDの低下に繋がるということを意識しておきましょう。

5. まとめ: AI時代の命綱として

AIがどれほど高速にコードを書いてくれるようになっても、それが「正しく動くこと」を証明するのは、テストであり、一番最初に行うのは単体テストです。
Vibeコーディング時代において、単体テストは 「AIの暴走を防ぐ命綱」 であり、エンジニアがシステムの品質に責任を持つための「砦」になるかもしれませんね。

次回は、「詳細設計」 の工程について、AI活用も交えてお話ししたいと思います。お楽しみに!

Discussion