tdd について
ユーティリティ、サービス、複雑なロジック、バグ修正にテスト駆動開発を適用するコーディングスキルです。Red-Green-Refactorの進め方とEffect向けのテストパターンを示します。
主な機能
- 01Red-Green-Refactorの手順
- 02テスト先行のバグ再現
- 03Effectサービスのテストパターン
- 04テスト範囲と配置のガイド
- 05リファレンスとサンプル
活用シーン
- 01ユーティリティや複雑な業務ロジックの開発
- 02再現テストを先に書いてバグを修正
- 03モックレイヤーを使ったEffectサービスのテスト
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ blogic-cz/ blogic-marketplace --skill tddインストール後、Claude Code で/tddを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/anti-patterns.mdcommands.mdeffect-tdd-patterns.mdred-green-refactor.mdtest-first-examples.md詳細
テスト駆動開発(TDD)
厳密なテストファースト開発を実行するには、このスキルを適用します。失敗するテストを書き、テストに合格する最小限のコードを実装してから、テストをグリーンに保ちながらリファクタリングします。
TDD を使うタイミングを判断する
次の判断表を使います。
| 状況 | このスキルを使うか? | デフォルトのアクション |
|---|---|---|
| 新しいユーティリティ関数 | はい | すぐに TDD サイクルを実行する |
| 新しい Effect サービス | はい | まずテストで動作を定義する |
| 複雑なビジネスロジック | はい | 実装前にテストで動作を固める |
| バグ修正 | はい | まず失敗するテストでバグを再現する |
| UI のスタイル/レイアウトのみの変更 | いいえ | 標準的な実装フローを使う |
| 探索的なプロトタイピング | いいえ | まずプロトタイプを作成し、動作が安定したら TDD に切り替える |
| TRPC エンドポイント(シンプルな CRUD) | 通常は使わない | デフォルトでは testing-patterns を使います。ユーザーがテストファースト/RGR を明示的に要求した場合のみ、このスキルを使います |
Red-Green-Refactor を実行する
一度に 1 つの動作を、次の 3 つのフェーズで進めます。
1. RED - まず失敗するテストを書く
import { describe, expect, it } from "vitest";
import { calculateDiscount } from "../calculate-discount";
describe("calculateDiscount", () => {
it("applies 10% discount for orders over 100", () => {
// This test fails first: function does not exist yet
expect(calculateDiscount(150)).toBe(135);
});
});
テストを実行して、失敗することを確認します。
bun run vitest run packages/common/src/__tests__/calculate-discount.test.ts
2. GREEN - 最小限のコードを実装する
テストに合格させるための最小限のコードを書きます。
// packages/common/src/calculate-discount.ts
export function calculateDiscount(amount: number): number {
if (amount > 100) {
return amount * 0.9;
}
return amount;
}
テストを実行して、合格することを確認します。
bun run vitest run packages/common/src/__tests__/calculate-discount.test.ts
3. REFACTOR - 動作を壊さずに改善する
テストをグリーンに保ちながら、コードの品質を改善します。
const DISCOUNT_THRESHOLD = 100;
const DISCOUNT_RATE = 0.1;
export function calculateDiscount(amount: number): number {
if (amount <= DISCOUNT_THRESHOLD) {
return amount;
}
return amount * (1 - DISCOUNT_RATE);
}
テストを再実行し、リファクタリングによって問題が生じていないことを確認します。
より詳しい例、失敗時の診断、反復的な拡張については、references/red-green-refactor.md を参照してください。
テストの範囲を絞る
まず軽量なテストを優先し、必要な場合にのみ段階を上げます。
- ユニットテスト(推奨) - 純粋関数と、モックレイヤーを使った Effect サービス。
- TRPC 統合テスト - TRPC と永続化の動作全体。
- E2E テスト - ブラウザー上のフローとユーザージャーニー。
| 状況 | テストの種類 | アクション |
|---|---|---|
| 純粋関数、パーサー、ユーティリティ | ユニット | すぐに書く |
| 依存関係のある Effect サービス | モックレイヤーを使ったユニットテスト | すぐに書く |
| TRPC プロシージャ(DB ロジック) | TRPC 統合テスト | testing-patterns の判断プロセスに従う |
| ユーザー向けのフロー、UI の動作 | E2E | testing-patterns の判断プロセスに従う |
Effect 固有の TDD パターンを適用する
明示的なレイヤーを使い、テストファーストでサービスを設計します。
- テストでインターフェースを定義する - 実装前に動作を明確にします。
- モックレイヤーを作成する - 依存関係を分離し、テストの決定性を保ちます。
- サービスを実装する - 最小限の動作でテストを満たします。
- リファクタリングする - 動作を維持しながら、読みやすさと構造を改善します。
import { describe, expect, it } from "@effect/vitest";
import { Effect, Layer } from "effect";
describe("PricingService", () => {
// 1. Define what the service should do via tests
it.effect("calculates base price without discount", () =>
Effect.gen(function* () {
const service = yield* PricingService;
const result = yield* service.calculatePrice({
itemId: "item-1",
quantity: 2,
});
expect(result.total).toBe(200);
}).pipe(Effect.provide(testLayer)),
);
it.effect("applies bulk discount for quantity > 10", () =>
Effect.gen(function* () {
const service = yield* PricingService;
const result = yield* service.calculatePrice({
itemId: "item-1",
quantity: 15,
});
expect(result.total).toBe(1350); // 15% discount
}).pipe(Effect.provide(testLayer)),
);
});
モックレイヤーファクトリーパターン
// Create parameterized mock layers for different test scenarios
const createMockInventoryLayer = (inventory: Map<string, number>) =>
Layer.succeed(InventoryService, {
getStock: (itemId) => Effect.succeed(inventory.get(itemId) ?? 0),
reserveStock: (itemId, qty) => Effect.succeed(void 0),
});
// Use in tests
const testLayer = PricingService.layer.pipe(
Layer.provide(createMockInventoryLayer(new Map([["item-1", 100]]))),
);
より詳しいガイダンス(エラーパスのテスト、レイヤーの合成、vi.mock() を避ける理由)については、references/effect-tdd-patterns.md を参照してください。
プロジェクトの慣例に沿ったテスト配置を使用する(例)
これらのパスはプロジェクトの慣例/例として扱い、リポジトリで異なるテスト配置が採用されている場合は適宜調整してください。
| コードの場所 | テストの場所 |
|---|---|
packages/X/src/file.ts | packages/X/src/__tests__/file.test.ts |
apps/web-app/src/infrastructure/trpc/routers/X.ts | apps/web-app/src/__tests__/X.test.ts |
apps/web-app/src/routes/** | apps/web-app/e2e/feature.e2e.ts |
詳細については参考資料を利用する
実行時にこのファイルを拡張するのではなく、対象を絞った参考資料を読み込んでください。
- 実行可能なコマンドのパターンについては、
references/commands.mdを使用してください。 - 失敗例の解説と修正については、
references/anti-patterns.mdを使用してください。
リソース
references/
red-green-refactor.md- 例を交えた詳細なTDDサイクルのワークフローeffect-tdd-patterns.md- Effectサービスのテスト、モックレイヤー、エラーケースtest-first-examples.md- このコードベースにおける、手順ごとのTDDの例commands.md- コマンドカタログと、正しい/誤った呼び出しパターンanti-patterns.md- 修正方法を含む、詳細なTDDのアンチパターン解説
関連スキル
testing-patterns- テスト構文、TRPC統合テスト、E2Eパターンeffect-ts- Effectサービスの設計、レイヤー、エラー処理