コンテンツにスキップ

2.1. 単体テスト規定

このドキュメントは、単体テスト(ユニットテスト)の実装に関する具体的な規約を定めます。単体テストは、品質保証の基盤であり、全てのテストの中で最も数が多く、最も高速に実行されるべきものです。

1. 単体テストの定義と目的

  • 定義:
    • 単体テストとは、アプリケーションを構成する最小単位のコンポーネント(クラス、メソッド、関数など)を、他の部分から隔離して検証するテストです。
  • 目的:
    • コンポーネントが、仕様通りの入力に対して、期待される出力を返すことを保証します。
    • リファクタリングに対するセーフティネットを提供し、コード変更の心理的安全性を高めます。
    • 開発サイクルの極めて早い段階でバグを検出し、修正コストを最小限に抑えます。

2. 隔離の原則 (Isolation)

単体テストの最も重要な原則は「隔離」です。テスト対象のユニットは、以下の外部依存から切り離されている必要があります。

  • データベース
  • ファイルシステム (ディスクI/O)
  • ネットワーク (外部API呼び出しなど)
  • 環境 (現在時刻、環境変数など)
  • 他のクラスやモジュール (テスト対象が直接依存しているものを除く)

これらの依存は、テストダブル (Test Doubles) を使用して置き換えます。

テストダブルの活用

  • モック (Mock):
    • 呼び出しを検証するためのオブジェクト。メソッドが「特定の引数で」「特定の回数」呼び出されたかを確認するために使用します。
  • スタブ (Stub):
    • テスト中に、決められた値を返すためのオブジェクト。依存コンポーネントからの戻り値を制御するために使用します。
  • フェイク (Fake):
    • 実際のロジックをよりシンプルな実装で置き換えたオブジェクト(例: インメモリデータベース)。

推奨ツール:

  • C#: Moq, NSubstitute
  • Python: unittest.mock (標準ライブラリ), pytest-mock
  • TypeScript/JavaScript: Jest (組み込み), Vitest (組み込み), Sinon.JS

3. 命名規則

テストメソッド名は、そのテストが「何を」「どのような状況で」「どう振る舞うべきか」が一目で分かるように、明確かつ統一された命名規則に従います。

  • 推奨フォーマット: [テスト対象メソッド名]_[テストシナリオ]_[期待される結果]
  • 区切り文字: アンダースコア _

良い例:

  • Sum_TwoPositiveNumbers_ReturnsCorrectSum (C#)
  • test_sum_with_negative_numbers_raises_exception (Python)
  • calculateTotal_should_return_zero_for_empty_cart (TypeScript)

悪い例:

  • Test1 (内容が全く不明)
  • SumTest (テスト対象は分かるが、シナリオと結果が不明)

4. 構造: AAAパターン

全ての単体テストは、Arrange-Act-Assert (AAA) パターンに従って構造化します。これにより、テストの可読性が大幅に向上します。

  • Arrange (準備): テストに必要なオブジェクトの生成、データの準備、モックの設定などを行います。
  • Act (実行): テスト対象のメソッドを呼び出します。
  • Assert (検証): 実行結果が期待通りであるかを、アサーションライブラリを使って検証します。
[Fact]
public void Sum_TwoPositiveNumbers_ReturnsCorrectSum()
{
    // Arrange
    var calculator = new Calculator();
    var x = 5;
    var y = 10;
    var expected = 15;

    // Act
    var actual = calculator.Sum(x, y);

    // Assert
    Assert.Equal(expected, actual);
}

5. アサーションの書き方

  • 1テストケース、1アサーションの原則 (One Assert Per Test):
    • 理想的には、1つのテストメソッドは、1つの論理的なアサーションのみを検証すべきです。これにより、テストが失敗した際に、原因が即座に特定できます。
    • ただし、1つのオブジェクトの複数のプロパティを検証する場合など、論理的に密接に関連する複数のアサーションを1つのテストに含めることは許容されます。
  • 具体的なアサーションメソッドの利用:
    • Assert.True(a == b) のような汎用的なアサーションではなく、Assert.Equal(a, b) のように、検証の意図が明確になる、より具体的なアサーションメソッドを使用します。これにより、失敗時のエラーメッセージが格段に分かりやすくなります。

6. テストすべきこと

以下の観点を参考に、網羅的なテストケースを作成します。

  • ハッピーパス (正常系): 最も一般的で、期待される入力に対する正常な動作。
  • エッジケース (境界値):
    • 数値: 0, 1, -1, 最大値, 最小値
    • 文字列: 空文字, null, 長い文字列, 特殊文字
    • コレクション: 空のコレクション, 1要素のみ, 多数の要素
  • エラーケース (異常系):
    • 不正な引数(null, 範囲外の値など)が与えられた場合に、期待される例外がスローされること。
    • 特定の条件下で、期待されるエラーコードやfalseが返されること。