Loading...
Loading...
LOAD THIS SKILL when: implementing new features with TDD, user mentions 'TDD', 'test-first', 'red-green-refactor', 'failing test', or when building Effect services that need thorough testing. Covers TDD methodology, Red-Green-Refactor cycle, Effect service testing with mock layers, and test-first development workflow.
npx skill4agent add blogic-cz/agent-tools 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 |
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);
});
});bun run vitest run packages/common/src/__tests__/calculate-discount.test.ts// 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.tsconst 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| 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 |
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]]))),
);references/effect-tdd-patterns.md| Code Location | Test Location |
|---|---|
| |
| |
| |
# Run all tests (unit + TRPC integration)
bun run test
# Watch mode - re-run on file changes
bun run test:watch
# Run with coverage report
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
# Run tests matching pattern
bun run vitest run -t "calculateDiscount"# Install Playwright browsers (first time only)
bun run test:e2e:install
# Run all E2E tests
bun run test:e2e
# Run E2E with interactive UI
bun run test:e2e:ui# These 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// ❌ BAD - Implementation before test
export function formatPrice(amount: number): string {
return `$${amount.toFixed(2)}`;
}
// Then writing test after - defeats TDD purpose// ❌ BAD - Test passes immediately (you didn't verify it can fail)
it("returns true", () => {
expect(true).toBe(true); // This always passes!
});// ❌ 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!// ❌ 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);
}red-green-refactor.mdeffect-tdd-patterns.mdtest-first-examples.mdtesting-patternseffect-ts