•12 min read

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

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

TDD (Test-Driven Development)

Click to reveal
Testing
実装コードを書く前にテストを書く開発手法。サイクル:Red(失敗するテスト)→ Green(テストをパスさせる)→ Refactor(テストをパスした状態を保ちながらクリーンアップする)。

TDD (Test-Driven Development)

TDD Cycle

Red Phase

Click to reveal
TDD Cycle
実装が存在しないため失敗するテストを書く。これにより、期待される振る舞いとAPIを定義する。

Red Phase

TDD Cycle

Green Phase

Click to reveal
TDD Cycle
テストをパスさせるために必要な最小限のコードを書く。それ以上でもそれ以下でもない — 余分な機能を追加したい衝動に抵抗する。

Green Phase

TDD Cycle

Refactor Phase

Click to reveal
TDD Cycle
すべてのテストがパスする状態を保ちながら、コードをクリーンアップし、重複を排除し、最適化し、設計を改善する。テストスイートはあなたのセーフティネットである。

Refactor Phase

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

長い間、私はほとんどの開発者と同じように、コードを書いた後にテストを書いていました。機能を完成させ、それなりに満足したら、それが意図した通りに動作することを確認するためにテストを書いていました。テストはいつもパスしていました。それが私の最初の間違いでした。

ある日の午後、私はユーティリティ関数をリファクタリングしました。クリーンアップして「より良く」したのですが、2日間気づかないうちに壊してしまいました。既存のテストは私の実装をテストしており、振る舞いをテストしていなかったのです。実装を変更すると、テストもそれに追従しました。QAが発見するまで、誰もそれに気づきませんでした。

その時、私は実際にTDDを試しました。誰かに言われたからではなく、その特定の失敗パターンにうんざりしていたからです。


Audio Briefing
0:00 / 0:00

TDDサイクル

フェーズアクションマインドセット目標
🔴 Red
失敗するテストを書く
まず期待される振る舞いを定義する
テストがAPIを記述する
🟢 Green
パスするための最小限のコードを書く
過剰な設計に抵抗する
テストをパスさせる — それ以上はしない
🔵 Refactor
設計を改善し、重複を排除する
テストはあなたのセーフティネットである
グリーンを保ちながらコードをクリーンにする

TDDは3つのステップを繰り返すリズムに従います。数回行えば、それは筋肉の記憶になりますが、最初の数回は逆行しているように感じるでしょう。

🔴 Red

実装が存在しないため失敗するテストを書きます。

🟢 Green

テストをパスさせるために必要な最小限のコードを書きます。

🔵 Refactor

テストがパスする状態を保ちながら、コードをクリーンアップし、重複を排除し、最適化します。

Advertisement

具体的な例:乗算

教科書的な例は小さいですが、ゆっくりと取り組む価値はあります。

ステップ1 — Red(失敗するテスト)

まずテストを書きます。それは失敗します — multiplyはまだ存在しません。それで構いません。それがポイントです。

test('multiplies two numbers', () => {
  expect(multiply(3, 4)).toBe(12);
});

ステップ2 — Green(パスさせる)

テストを満たすのに十分なコードだけを書きます。それ以上は書きません。早期最適化はしません。

function multiply(a, b) {
  return a * b;
}

ステップ3 — Refactor

ここではコードはすでにクリーンです。しかし、物事が大きくなるにつれて、重複を排除し、命名を改善し、抽象化をクリーンアップする場所になります — テストは何も壊れていないことを確認しながら。

型安全性を追加した後の、正しさへのリファクタリングは次のようになります。

javascript
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
Advertisement

こちらもおすすめ

Share this article:

Stay Updated

Get the latest posts delivered straight to your inbox.

Free Developer Utilities

Free In-Browser Developer Tools

Clean AI CLI logs, build cron expressions, decode JWTs, and calculate chmod permissions offline.

Explore Tools
Advertisement