2部 xUnit
18章 xUnitへ向かう小さな一歩
自分の使う道具すらTDDで作れる
TDDの全体像を理解するには
“テストフレームワークがどう設計されるか”を体験するのが一番だから
結果として、テストフレームワーク自体が一つ一つを育てていった出来上がったフレームワークであることを実感する。
19章 前準備
前準備が増えるとテストは“読めなくなり、書けなくなり、捨てられなくなる”
前準備とは
- テスト対象を作る
- DBの準備をする
- ダミーデータを入れる
- 状態を整える
悪い例
$user = new User();
$user->setId(1);
$user->setName("John");
$user->setEmail("john@example.com");
$user->setVerified(true);
$cart = new Cart();
$cart->setUser($user);
$cart->addItem("item1", 3);
$cart->addItem("item2", 2);
$orderService->placeOrder($cart);
- テストの本題が埋もれる
- 何を検証しているかわからない
- 少し仕様変えるだけで大量崩壊
テストを楽にする設計とは
テストが書きやすい設計 = 良い設計
セットアップ削減
クラス分割できないか
責務を減らせないか
デフォルト状態を持たせられないか
Value Object化できないか
20章 後片付け
テストを“書きやすくする”だけでなく、“捨てやすく・壊れにくく・安全に保つ”技術
Teardownとは:
- ファイル削除
- DBクリーンアップ
- グローバル状態リセット
- singletonリセット
- Cookie / Session / Cache 消去
後片付けを放置すると
- テストが壊れだす
- テストが信用されなくなる
“後片付けを減らす”ための設計技術
- ValueObject
final readonly class Money {}
- 純粋関数
function calcTotal(Order $order): Money {}
- テストダブル
FakeRepository
- トランザクションをRollbackする
use Illuminate\Foundation\Testing\RefreshDatabase;
など。
21章 数え上げ
システム内の“状態”や“振る舞い”を、テスト可能な 有限個のパターン に分解
なぜ、必要なのか?
- 条件が無限に増える
- テストがなんとなく網羅的に見える
例:多国籍通貨の場合
状態
- 同一通貨
- 異なる通貨
演算
- plus
- times
とするとテスト総数は
(状態2種 × 演算2種 × 代表値)
数え上げの黄金ルール
- 仕様は状態数に落とす
- 状態はenumで表現
enum OrderStatus {
case DRAFT;
case CONFIRMED;
case SHIPPED;
}
- enum = テスト対象
- 増えすぎたら設計を見直す
数え上げは網羅のためではなく、設計をシンプルに保つためにある
22章 失敗の扱い
失敗はバグではなく、設計を導くシグナルとして扱う
失敗を3種類に分ける
-
想定内の失敗
テストが要求している実装が間違っていない証拠 -
想定外の失敗
- 期待値と実測値がずれる
- 条件漏れ
- 仕様誤解
→設計理解がずれているサイン
-
テスト自体の失敗
- テストデータの間違い
- 期待値間違い
- 前提条件がおかしい
→テストがバグってる
ダメな失敗の扱い
- testをコメントアウト →品質崩壊
- assertをごまかす →テストが嘘になる
- ignoreにする。 →CIが腐る
良い失敗の扱い
- 必ず止まって読む
- 修正の主役は設計
TDDにおける失敗とは品質不良ではなく、設計探査の途中経過である。
23章 スイートにまとめる
テストは「個々で完璧」で終わらせず、“意味のある集まり”として構造化することで洗練される
テストスイートはプロジェクトの品質保証書
テストが何を保証するかを読める構造
スイート設計パターンのについて
- レイヤースイート
Unit ← domain / 純ロジック
Integration ← DB・API・外部I/O
E2E ← 画面 / 実利用フロー
それぞれの役割
| スイート | 責務 | 特徴 |
|---|---|---|
| Unit | ロジック正当性 | 超高速、Fakeのみ |
| Integration | I/O整合性 | 遅いが安定 |
| E2E | UX保証 | 最も遅く不安定 |
実行戦略
| フェーズ | 実行 |
|---|---|
| ローカルTDD | Unit |
| PR検証 | Unit + Integration |
| マージ/夜間 | 全部 |
ドメインスイート
- テストが仕様書代わりになる
- 影響範囲が一瞬で分かる
tests/
└─ Domain/
├─ Order/
│ ├─ OrderTest.php
│ └─ CancelTest.php
└─ Payment/
├─ MoneyTest.php
└─ ExchangeTest.php
ユースケーススイート
- ユーザー操作単位でまとめる
tests/
└─ UseCase/
├─ PurchaseFlowTest.php
├─ CancelFlowTest.php
└─ RefundFlowTest.php
コンポーネントスイート
- マイクロサービスやモジュール境界テスト
tests/
├─ UserServiceSuite/
└─ PaymentServiceSuite/
24章 xUnit全体のふりかえり
xUnitは目的ではなく“TDDという思考習慣を身につけるための教材”である
なぜxUnitを「作った」のか
1.テスト駆動そのものを体験するため
2.役割分離を学ぶため
3.役割分離を学ぶため