テスト駆動開発(TDD)入門

Table of Contents
TDD (Test-Driven Development)
TDD (Test-Driven Development)
Red Phase
Red Phase
Green Phase
Green Phase
Refactor Phase
Refactor Phase
テスト駆動開発(TDD)入門
長い間、私はほとんどの開発者と同じように、コードを書いた後にテストを書いていました。機能を完成させ、それなりに満足したら、それが意図した通りに動作することを確認するためにテストを書いていました。テストはいつもパスしていました。それが私の最初の間違いでした。
ある日の午後、私はユーティリティ関数をリファクタリングしました。クリーンアップして「より良く」したのですが、2日間気づかないうちに壊してしまいました。既存のテストは私の実装をテストしており、振る舞いをテストしていなかったのです。実装を変更すると、テストもそれに追従しました。QAが発見するまで、誰もそれに気づきませんでした。
その時、私は実際にTDDを試しました。誰かに言われたからではなく、その特定の失敗パターンにうんざりしていたからです。
TDDサイクル
| フェーズ | アクション | マインドセット | 目標 |
|---|---|---|---|
| 🔴 Red | 失敗するテストを書く | まず期待される振る舞いを定義する | テストがAPIを記述する |
| 🟢 Green | パスするための最小限のコードを書く | 過剰な設計に抵抗する | テストをパスさせる — それ以上はしない |
| 🔵 Refactor | 設計を改善し、重複を排除する | テストはあなたのセーフティネットである | グリーンを保ちながらコードをクリーンにする |
TDDは3つのステップを繰り返すリズムに従います。数回行えば、それは筋肉の記憶になりますが、最初の数回は逆行しているように感じるでしょう。
🔴 Red
🟢 Green
🔵 Refactor
具体的な例:乗算
教科書的な例は小さいですが、ゆっくりと取り組む価値はあります。
ステップ1 — Red(失敗するテスト)
まずテストを書きます。それは失敗します — multiplyはまだ存在しません。それで構いません。それがポイントです。
test('multiplies two numbers', () => {
expect(multiply(3, 4)).toBe(12);
});
ステップ2 — Green(パスさせる)
テストを満たすのに十分なコードだけを書きます。それ以上は書きません。早期最適化はしません。
function multiply(a, b) {
return a * b;
}
ステップ3 — Refactor
ここではコードはすでにクリーンです。しかし、物事が大きくなるにつれて、重複を排除し、命名を改善し、抽象化をクリーンアップする場所になります — テストは何も壊れていないことを確認しながら。
型安全性を追加した後の、正しさへのリファクタリングは次のようになります。
11 function multiply(a, b) {2+ if (typeof a !== 'number' || typeof b !== 'number') {3+ throw new TypeError('Both arguments must be numbers');4+ }25 return a * b;36 }
なぜ実際に機能するのか
私は、要件が不明確なコードの一部でTDDを試すまで、設計上の利点について懐疑的でした。まずテストを書くことで、私は「これは実際に何をすべきか?」と問うことを余儀なくされました。ロジックを書く前にインターフェースを定義しなければなりませんでした。それが、より困難で、より重要な問いであることが判明しました。
メリット
- バグが少ない — 問題はQAで数日後に見つかるのではなく、即座に捕捉されます。
- より良い設計 — 実装の前にインターフェースを定義します。この順序が重要です。
- 恐れを知らないリファクタリング — 堅牢なテストスイートがあれば、息を止めることなくコードを変更できます。
- 生きたドキュメント — テストはコードがどのように振る舞うかを正確に記述します。それらは嘘をつきません。
| TDDなし | TDDあり | |
|---|---|---|
| バグの発見 | 本番環境または手動テストで発見される | 開発中に即座に捕捉される |
| 設計 | 実装がAPI設計を駆動する | APIが最初に設計される(テストファースト) |
| リファクタリング | 危険 — セーフティネットがない | 自信を持って — テストが振る舞いを検証する |
| ドキュメント | 古くなる別のドキュメント | テスト = 常に最新のドキュメント |
私が最初に犯した間違い
- テストを実行する前にコードを書きすぎた。
- リファクタリングステップを完全にスキップした(それはオプションではない)。
- 外部の振る舞いではなく、実装の詳細をテストした — これらのテストはリファクタリングのたびに壊れる。
- コードの後にテストを書き、それをTDDと呼んだ。それは単なるテストである。
どこから始めるか
小さく始めましょう。ユーティリティ関数 — フォーマッター、バリデーター、何か自己完結型のもの — を選びましょう。まずテストを書き、それが失敗するのを見て、それからパスさせます。ワンサイクルです。どんな感じか見てみましょう。
設計上の利点は、3行の関数では現れません。それは、真の複雑さを持つものを構築しているときに現れ、テストがAPIがどこで混乱しているか、ロジックが多すぎる責任を負っているかを常に教えてくれるときに現れます。それがTDDの仕事です。
TDD Testing JavaScript Best Practicesこちらもおすすめ
Free In-Browser Developer Tools
Clean AI CLI logs, build cron expressions, decode JWTs, and calculate chmod permissions offline.
Related Articles

JavaScriptにおける単体テスト:本番環境のバグを実際に防ぐ実践的なパターン
AAAパターン、テストフィクスチャ、fast-checkによるプロパティベーステスト、境界モックなど、堅牢なJavaScript単体テストを作成するための実用的なガイド。
Read more
PlaywrightによるE2Eテスト習得2026年版
Playwrightのauto-waiting、browser context isolation、network interception、auth storage、CI parallelizationを活用し、E2Eテストを習得するためのガイドです。
Read more
TypeScriptジェネリクス: 型安全なAPIのための高度なパターン
条件型、マップ型、テンプレートリテラル型、branded type、判別共用体など、TypeScriptジェネリクスの高度なパターンを習得し、型安全なプロダクションライブラリを構築しましょう。
Read more