tdd
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseTest-Driven Development (TDD)
测试驱动开发(TDD)
Philosophy
理念
TDD is a development methodology where you write tests BEFORE implementation. This ensures:
- Clear requirements - Tests define expected behavior upfront
- Better design - Forces you to think about interfaces first
- Confidence - Every feature has test coverage from day one
- Refactoring safety - Tests catch regressions immediately
TDD是一种先编写测试再实现功能的开发方法论,它能确保:
- 需求清晰:测试会预先定义预期行为
- 更优设计:强制你优先考虑接口设计
- 开发信心:每个功能从第一天起就有测试覆盖
- 重构安全:测试能立即发现回归问题
When to Use TDD
何时使用TDD
| Situation | Use TDD? | Reason |
|---|---|---|
| New utility function | YES | Pure functions are perfect for TDD |
| New Effect service | YES | Define interface via tests first |
| Complex business logic | YES | Tests clarify requirements |
| Bug fix | YES | Write failing test that reproduces bug first |
| UI component styling | No | Visual changes don't benefit from TDD |
| Exploratory prototyping | No | Requirements unclear, iterate first |
| TRPC endpoint (simple CRUD) | Optional | Ask user preference |
| 场景 | 是否使用TDD | 理由 |
|---|---|---|
| 新增工具函数 | 是 | 纯函数非常适合用TDD开发 |
| 新增Effect服务 | 是 | 优先通过测试定义接口 |
| 复杂业务逻辑 | 是 | 测试能明确需求 |
| 修复Bug | 是 | 先编写能复现Bug的失败测试 |
| UI组件样式 | 否 | 视觉改动无法从TDD中获益 |
| 探索性原型开发 | 否 | 需求不清晰,优先快速迭代 |
| TRPC端点(简单CRUD) | 可选 | 询问用户偏好 |
Red-Green-Refactor Cycle
红-绿-重构周期
The TDD workflow follows three phases:
TDD工作流遵循三个阶段:
1. RED - Write Failing Test First
1. 红(RED) - 先编写失败的测试
typescript
import { describe, expect, it } from "vitest";
import { calculateDiscount } from "../calculate-discount";
describe("calculateDiscount", () => {
it("applies 10% discount for orders over 100", () => {
// This test will FAIL - function doesn't exist yet
expect(calculateDiscount(150)).toBe(135);
});
});Run test to see it fail:
bash
bun run vitest run packages/common/src/__tests__/calculate-discount.test.tstypescript
import { describe, expect, it } from "vitest";
import { calculateDiscount } from "../calculate-discount";
describe("calculateDiscount", () => {
it("applies 10% discount for orders over 100", () => {
// This test will FAIL - function doesn't exist yet
expect(calculateDiscount(150)).toBe(135);
});
});运行测试确认它会失败:
bash
bun run vitest run packages/common/src/__tests__/calculate-discount.test.ts2. GREEN - Minimal Implementation
2. 绿(GREEN) - 最小化实现
Write the minimum code to make the test pass:
typescript
// packages/common/src/calculate-discount.ts
export function calculateDiscount(amount: number): number {
if (amount > 100) {
return amount * 0.9;
}
return amount;
}Run test to see it pass:
bash
bun run vitest run packages/common/src/__tests__/calculate-discount.test.ts编写最少的代码让测试通过:
typescript
// packages/common/src/calculate-discount.ts
export function calculateDiscount(amount: number): number {
if (amount > 100) {
return amount * 0.9;
}
return amount;
}运行测试确认通过:
bash
bun run vitest run packages/common/src/__tests__/calculate-discount.test.ts3. REFACTOR - Improve Without Breaking
3. 重构(REFACTOR) - 不破坏功能的前提下优化代码
Improve code quality while keeping tests green:
typescript
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);
}Run tests again to verify refactoring didn't break anything.
See for detailed workflow examples.
references/red-green-refactor.md在保持测试通过的前提下提升代码质量:
typescript
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.mdTest Hierarchy (Prefer Simpler)
测试层级(优先选择更简单的方案)
- Unit tests (preferred) - Pure functions, Effect services with mock layers
- TRPC Integration (ask first) - Full TRPC stack with PGlite
- E2E (ask + justify) - Browser automation, slowest
| Situation | Test Type | Action |
|---|---|---|
| Pure function, parser, util | Unit | Write immediately |
| Effect service with dependencies | Unit with mock layers | Write immediately |
| TRPC procedure (DB logic) | TRPC Integration | Ask user first |
| User-facing flow, UI behavior | E2E | Ask + warn about maintenance |
- 单元测试(首选) - 纯函数、带模拟层的Effect服务测试
- TRPC集成测试(先询问) - 基于PGlite的完整TRPC栈测试
- E2E测试(需要询问+说明理由) - 浏览器自动化测试,执行速度最慢
| 场景 | 测试类型 | 操作建议 |
|---|---|---|
| 纯函数、解析器、工具函数 | 单元测试 | 直接编写 |
| 带依赖的Effect服务 | 带模拟层的单元测试 | 直接编写 |
| TRPC过程(数据库逻辑) | TRPC集成测试 | 先询问用户 |
| 用户面向流程、UI行为 | E2E测试 | 先询问+提示维护成本 |
Effect TDD Patterns
Effect TDD模式
Test-First Service Design
测试先行的服务设计
- Define interface via test - What should the service do?
- Create mock layer - Isolate dependencies
- Implement service - Make tests pass
- Refactor - Improve with confidence
typescript
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)),
);
});- 通过测试定义接口 - 明确服务需要实现什么功能?
- 创建模拟层 - 隔离依赖
- 实现服务 - 让测试通过
- 重构 - 放心优化代码
typescript
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)),
);
});Mock Layer Factory Pattern
模拟层工厂模式
typescript
// 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]]))),
);See for comprehensive Effect testing patterns.
references/effect-tdd-patterns.mdtypescript
// 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]]))),
);查看 获取完整的Effect测试模式。
references/effect-tdd-patterns.mdTest File Locations
测试文件位置
| Code Location | Test Location |
|---|---|
| |
| |
| |
| 代码位置 | 测试位置 |
|---|---|
| |
| |
| |
Commands
命令
Unit & Integration Tests
单元&集成测试
bash
undefinedbash
undefinedRun all tests (unit + TRPC integration)
运行所有测试(单元 + TRPC集成)
bun run test
bun run test
Watch mode - re-run on file changes
监听模式 - 文件改动时自动重跑测试
bun run test:watch
bun run test:watch
Run with coverage report
运行测试并生成覆盖率报告
bun run test:coverage
bun run test:coverage
Run specific test file (FROM PROJECT ROOT, full path required)
运行指定测试文件(需要在项目根目录执行,传入完整路径)
bun run vitest run packages/common/src/tests/pagination.test.ts
bun run vitest run apps/web-app/src/tests/formatters.test.ts
bun run vitest run packages/common/src/tests/pagination.test.ts
bun run vitest run apps/web-app/src/tests/formatters.test.ts
Run tests matching pattern
运行匹配指定关键词的测试
bun run vitest run -t "calculateDiscount"
undefinedbun run vitest run -t "calculateDiscount"
undefinedE2E Tests
E2E测试
bash
undefinedbash
undefinedInstall Playwright browsers (first time only)
安装Playwright浏览器(仅首次执行需要)
bun run test:e2e:install
bun run test:e2e:install
Run all E2E tests
运行所有E2E测试
bun run test:e2e
bun run test:e2e
Run E2E with interactive UI
带交互式UI运行E2E测试
bun run test:e2e:ui
undefinedbun run test:e2e:ui
undefinedWRONG Syntax (DO NOT USE)
错误语法(请勿使用)
bash
undefinedbash
undefinedThese DO NOT work:
这些命令无法正常运行:
bun run test packages/common/src/tests/file.test.ts # script doesn't accept path
cd packages/common && bun run vitest run src/tests/file.test.ts # wrong cwd
---bun run test packages/common/src/tests/file.test.ts # 脚本不接收路径参数
cd packages/common && bun run vitest run src/tests/file.test.ts # 工作目录错误
---TDD Anti-Patterns
TDD反模式
1. Writing Implementation First
1. 先写实现再写测试
typescript
// ❌ BAD - Implementation before test
export function formatPrice(amount: number): string {
return `$${amount.toFixed(2)}`;
}
// Then writing test after - defeats TDD purposetypescript
// ❌ 错误 - 先实现功能再写测试
export function formatPrice(amount: number): string {
return `$${amount.toFixed(2)}`;
}
// 之后再补测试 - 违背了TDD的初衷2. Skipping the RED Phase
2. 跳过红阶段
typescript
// ❌ BAD - Test passes immediately (you didn't verify it can fail)
it("returns true", () => {
expect(true).toBe(true); // This always passes!
});typescript
// ❌ 错误 - 测试一开始就会通过(你没有验证它可以失败)
it("returns true", () => {
expect(true).toBe(true); // This always passes!
});3. Too Many Tests at Once
3. 一次性编写太多测试
typescript
// ❌ BAD - Writing all tests before any implementation
describe("UserService", () => {
it("creates user", () => {
/* ... */
});
it("updates user", () => {
/* ... */
});
it("deletes user", () => {
/* ... */
});
it("lists users", () => {
/* ... */
});
it("validates email", () => {
/* ... */
});
// 10 more tests...
});
// Now you have 15 failing tests - overwhelming!Correct approach: One test at a time. RED → GREEN → REFACTOR → next test.
typescript
// ❌ 错误 - 还没写任何实现就写完所有测试
describe("UserService", () => {
it("creates user", () => {
/* ... */
});
it("updates user", () => {
/* ... */
});
it("deletes user", () => {
/* ... */
});
it("lists users", () => {
/* ... */
});
it("validates email", () => {
/* ... */
});
// 还有10个测试...
});
// 现在你有15个失败的测试 - 压力太大了!正确做法:一次只写一个测试,完成红→绿→重构循环后再写下一个测试。
4. Skipping Refactor Phase
4. 跳过重构阶段
typescript
// ❌ BAD - Test passes, move on without cleanup
export function calc(a: number, b: number, c: string): number {
if (c === "add") return a + b;
if (c === "sub") return a - b;
if (c === "mul") return a * b;
return 0;
}
// ✅ GOOD - Refactor to cleaner design
type Operation = "add" | "subtract" | "multiply";
const operations: Record<Operation, (a: number, b: number) => number> = {
add: (a, b) => a + b,
subtract: (a, b) => a - b,
multiply: (a, b) => a * b,
};
export function calculate(a: number, b: number, op: Operation): number {
return operations[op](a, b);
}typescript
// ❌ 错误 - 测试通过就不管了,不做代码清理
export function calc(a: number, b: number, c: string): number {
if (c === "add") return a + b;
if (c === "sub") return a - b;
if (c === "mul") return a * b;
return 0;
}
// ✅ 正确 - 重构为更清晰的设计
type Operation = "add" | "subtract" | "multiply";
const operations: Record<Operation, (a: number, b: number) => number> = {
add: (a, b) => a + b,
subtract: (a, b) => a - b,
multiply: (a, b) => a * b,
};
export function calculate(a: number, b: number, op: Operation): number {
return operations[op](a, b);
}Resources
资源
references/
references/目录下文件
- - Detailed TDD cycle workflow with examples
red-green-refactor.md - - Effect service testing, mock layers, error cases
effect-tdd-patterns.md - - Step-by-step TDD examples for this codebase
test-first-examples.md
- - 带示例的详细TDD周期工作流
red-green-refactor.md - - Effect服务测试、模拟层、错误场景处理
effect-tdd-patterns.md - - 本代码库的分步TDD示例
test-first-examples.md
Related Skills
相关技能
- - Test syntax, TRPC integration tests, E2E patterns
testing-patterns - - Effect service design, layers, error handling
effect-ts
- - 测试语法、TRPC集成测试、E2E模式
testing-patterns - - Effect服务设计、层、错误处理
effect-ts