テスト戦略
はじめに
あなたのコードは本当に「問題ない」ですか? コードを変更するたびに手動でクリックして壊れていないか確認する——プロジェクトが小さいうちはこれでも何とかなりますが、コードが数万行に成長し、チームが10人以上に拡大すると、「手動でのクリック確認」は災難となります。
この章では、ソフトウェアテストの核心的な戦略を理解し、テストピラミッドからTDDまで、体系的な品質保証のマインドセットを構築します。
この記事で何を学ぶか?
| 章 | 内容 | 主要概念 |
|---|---|---|
| 第 1 章 | テストピラミッド | テストのレベルと比率 |
| 第 2 章 | ユニットテストの実践 | 良いテストの書き方 |
| 第 3 章 | TDD(テスト駆動開発) | レッド・グリーン・リファクタサイクル |
| 第 4 章 | テスト戦略の選択 | 異なるシナリオに応じたアプローチ |
この章を終えると、プロジェクトに適したテスト戦略を選択し、価値のあるテストを書き、TDDを通じてコードの設計品質を向上させる方法を理解できるようになります。
0. 全体像:なぜ自動化テストが必要なのか?
あなたが建築エンジニアだと想像してください。図面を修正するたびに、毎回すべての階に登って構造の安全性を確認するわけではありません——自動化された検査システムに頼ります。ソフトウェアテストは、コードの世界における「構造検査システム」です。
自動化テストの価値
- リグレッション防止:機能Aを変更した際、機能B、C、Dが影響を受けていないか自動検出
- リファクタリングの信頼:テストカバレッジがあるコードは、安心してリファクタリングできる
- 生きたドキュメント:良いテストは最良の使用説明書
- 迅速なフィードバック:数秒でコードが正しいかどうかを把握でき、デプロイ後まで問題の発見を待つ必要がない
1. テストピラミッド:テストのレベルと比率
1.1 三層のピラミッド
Mike Cohn が提唱したテストピラミッドは、テスト戦略の古典的なモデルです。それは次のことを教えています:異なる種類のテストには異なる数量の比率があるべきです。
下のインタラクティブコンポーネントで、ピラミッドの各層をクリックしてその特徴を学びましょう:
1.2 なぜピラミッド型なのか?
ピラミッドの形は、核心的なトレードオフを反映しています:速度とリアリズムのバランス。
- 下層(ユニットテスト):極めて高速、数が最も多い、コストが最も低いが、個々の部品のみを検証
- 中層(インテグレーションテスト):速度は中程度、数も中程度、部品間の連携を検証
- 上層(E2Eテスト):実際のユーザーに最も近いが、遅く、メンテナンスコストが高く、環境の問題で失敗しやすい
アンチパターン:アイスクリームコーン —— プロジェクトでE2Eテストが最も多く、ユニットテストが最も少ない場合、それは逆さまの「アイスクリームコーン」です。これは、テストスイートの実行が遅く、頻繁に失敗し、メンテナンスコストが極めて高いことを意味します。
2. ユニットテストの実践
2.1 良いユニットテストとは?
良いユニットテストは FIRST 原則に従います:
| 原則 | 意味 | 説明 |
|---|---|---|
| Fast(高速) | 速い | ミリ秒で完了し、開発者が頻繁に実行したくなる |
| Independent(独立) | 独立している | テスト間で相互依存がなく、単独で実行可能 |
| Repeatable(反復可能) | 再現可能 | どの環境で実行しても同じ結果になる |
| Self-validating(自己検証) | 自己完結 | 結果が明確な合格/不合格で、人間の判断が不要 |
| Timely(適時) | タイムリー | コードと同時、あるいはその前にテストを書く |
2.2 テストの構造:AAA パターン
すべてのテストは明確な3部構成を持つべきです:
test('税込み価格を正しく計算すること', () => {
// Arrange(準備)—— テストデータの設定
const price = 100
const taxRate = 0.13
// Act(実行)—— テスト対象関数の呼び出し
const result = calculateTotalWithTax(price, taxRate)
// Assert(検証)—— 結果の確認
expect(result).toBe(113)
})2.3 何をテストするか?何をテストしないか?
テストすべきもの:
- コアビジネスロジック(価格計算、権限判定、データ変換)
- 境界条件(null、ゼロ、負の数、極端に大きな数)
- エラー処理パス
テスト不要なもの:
- サードパーティライブラリの内部実装
- 単純な getter/setter
- フレームワーク自体の機能(例:Vueのリアクティブシステム)
3. TDD:テスト駆動開発
3.1 レッド・グリーン・リファクタサイクル
TDD(Test-Driven Development)の核心はシンプルなサイクルです:先にテストを書き、次に実装を書き、最後にリファクタリングする。
下のインタラクティブコンポーネントで、TDDの完全なサイクルを体験しましょう:
test('add(1, 2) should return 3', () => {
expect(add(1, 2)).toBe(3)
})3.2 TDDの三つのルール
- 失敗するテストを通すため以外に、製品コードを書いてはいけない
- テストが失敗するのに十分なテストコードだけを書く(コンパイルエラーも失敗に含む)
- テストを通すのに十分な製品コードだけを書く
3.3 TDDの真の価値
TDDの価値は「先にテストを書くこと」にとどまらず、インターフェース設計について考えることを強制する点にあります。先にテストを書くということは、「利用者」の視点で考えることです:この関数はどのようなパラメータを受け取るべきか?何を返すべきか?これは自然により良いAPI設計につながります。
TDDは銀の弾丸ではない
TDDはロジックが多いコード(アルゴリズム、ビジネスルール、データ変換)に適していますが、UIレイアウトや探索的プロトタイプなどのシナリオでは、TDDを強制すると逆に速度が低下します。重要なのは、その思想を理解し、柔軟に適用することです。
4. テスト戦略の選択
4.1 プロジェクトタイプ別のテスト重点
| プロジェクトタイプ | テストの重点 | 推奨比率 |
|---|---|---|
| ユーティリティライブラリ/SDK | ユニットテスト中心 | 90% ユニット + 10% インテグレーション |
| API サービス | インテグレーションテスト中心 | 30% ユニット + 60% インテグレーション + 10% E2E |
| Web アプリケーション | バランス型 | 50% ユニット + 30% インテグレーション + 20% E2E |
| MVP/プロトタイプ | クリティカルパスのE2E | 少量のコアテストのみ |
4.2 一般的なテストツール
| ツール | タイプ | 適用シナリオ |
|---|---|---|
| Vitest | ユニット/インテグレーション | Viteプロジェクトの第一選択、Jest APIと互換 |
| Jest | ユニット/インテグレーション | Node.jsエコシステムで最も人気 |
| Playwright | E2E | クロスブラウザ、Microsoft製 |
| Cypress | E2E | 開発者体験が良く、デバッグが容易 |
| Testing Library | コンポーネントテスト | ユーザーの視点からUIコンポーネントをテスト |
5. AI 活用:大規模言語モデルでテスト効率を向上
LLMはテスト分野で非常に強力な能力を発揮しています——テストケースの生成、境界条件の発見、完全なテストコードの作成を支援できます。
5.1 ユニットテストの生成
プロンプト:
以下の関数に対して、Vitestフレームワークを使用してユニットテストを 書いてください。要件: 1. AAAパターン(Arrange-Act-Assert)に従う 2. 正常パス、境界条件、エラーパスをカバー 3. 各テストケースに明確な説明を付ける [関数のコードを貼り付け]
5.2 境界条件の発見
プロンプト:
以下の関数を分析し、すべての可能な境界条件と極端な入力シナリオを リストしてください。含む:null、ゼロ、負の数、極端に大きな数、 特殊文字、同時実行の状況など。 各シナリオについて、期待される動作と潜在的なリスクを説明してください。 [関数のコードを貼り付け]
5.3 要件からテストを生成(TDD支援)
プロンプト:
ショッピングカートモジュールを実装したいです。要件: - 商品の追加、削除、数量変更 - 合計金額の自動計算(割引込み) - 在庫不足時のエラー表示 TDDのアプローチに従い、まずテストケースのみを書いてください (実装は書かない)。Vitestを使用し、すべてのコアシナリオを カバーしてください。
AI 活用のアドバイス
AIが生成したテストのアサーションが意味のあるものであるか確認してください——expect(true).toBe(true) のような無意味なテストを避ける。良いテストは、コードにエラーがあれば実際に失敗するべきです。
6. まとめ
- テストピラミッド:下層は多く、上層は少なく、速度とリアリズムのバランス
- ユニットテスト:FIRST原則とAAAパターンに従い、コアロジックをテスト
- TDD:レッド・グリーン・リファクタサイクルで、テストが設計を駆動
- 戦略の選択:プロジェクトのタイプと段階に応じて、適切なテスト比率を選択
最後に
テストは負担ではなく、加速器です。短期的には、テストを書くことで余分な時間がかかりますが、長期的には、無数の手動確認、リグレッションバグの調査、深夜の緊急修正の時間を節約できます。良いテストがあれば、こう言える自信が持てます:「安心して変更してください。テストが問題がないか教えてくれます。」
さらに学ぶために
- 古典的書籍:Kent Beck『テスト駆動開発』はTDDの古典です。
- 実践ガイド:小さなプロジェクトでVitestを使ってテストを書き、ゼロからのテストプロセスを体験する。
- テストパターン:Mock、Stub、Spyの違いと使用シナリオを学ぶ。
- 継続的インテグレーション:テストをCI/CDパイプラインに統合し、毎回のコミットで自動実行する。