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が返されること。