実装ではなく振る舞いをテストする原則とパターン
「実装ではなく振る舞いをテストする。ビジネス動作を通じて100%カバレッジを達成し、実装の詳細ではない」
コードが何をするかを検証し、どうやるかを検証しない。
意図されたインターフェースを通じて振る舞いを実行する。内部リファクタリング後もテストが有効なままになる。実装の変更ではなく、真のバグを検出する。
beforeEach を使った可変共有状態の代わりに、Partial<T> オーバーライドを受け取るファクトリ関数を使用する。
各テストは、本番のスキーマに対して検証された、新鮮で完全なオブジェクトを取得する。テスト用に再定義されたスキーマではない。
// Good
function createUser(overrides: Partial<User> = {}): User {
return {
id: 'test-id',
name: 'Test User',
email: '[email protected]',
...overrides,
}
}
test('ユーザー作成', () => {
const user = createUser({ name: 'Alice' })
// ...
})
誤解を招くパターンに注意:
実装ファイルではなく、ユーザーワークフローを中心にテストを構造化する。
単一の振る舞いテストファイルが、1:1 のファイルマッピングなしで複数の内部モジュールを実行できる。
beforeEach で let を使用する(共有可変状態を作成)test('バリデータが呼ばれる', () => {
const validateSpy = jest.spyOn(validator, 'validate')
processPayment(amount)
expect(validateSpy).toHaveBeenCalled()
})
test('負の金額を拒否する', () => {
const result = processPayment(-100)
expect(result.error).toBe('金額は正の値である必要があります')
})
test('有効な支払いを処理する', () => {
const result = processPayment(1000)
expect(result.success).toBe(true)
expect(result.transactionId).toBeDefined()
})
振る舞いを通じてバリデーションレイヤー全体を検証する。バリデータ関数が呼ばれたかではなく、processPayment() が負の金額を拒否し、適切なエラーメッセージを返すことをテストする。