ワンクリックで
tdd
Test-Driven Development with Red-Green-Refactor cycle, vertical slices, and tracer bullets
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Test-Driven Development with Red-Green-Refactor cycle, vertical slices, and tracer bullets
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
Diagnose and fix build/CI failures automatically
Concurrent code generation via multi-model picker
UI/UX design and frontend component generation
OMP self-improvement — analyse own skills/agents and propose improvements
Visual diff/screenshot comparison verdict
Clone and adapt a web page/design to the codebase
| name | tdd |
| description | Test-Driven Development with Red-Green-Refactor cycle, vertical slices, and tracer bullets |
| triggers | ["/omp:tdd",{"tdd":null}] |
Use this skill when implementing any feature or bugfix using the Red-Green-Refactor cycle. Write the failing test first, write minimal code to pass it, then refactor.
The core TDD loop has exactly three phases. Never skip or reorder them.
Write a test that describes the behavior you want. Run it — it must fail. A test that passes before any implementation is written is not a test, it is a false signal.
Write the smallest amount of production code that makes the test pass. Do not write code for future requirements. Do not generalize. Make the test green and stop.
With the test green, clean up the implementation — remove duplication, improve naming, extract deep modules. Run tests after every edit. If they go red, revert immediately.
Repeat this cycle for each behavioral slice.
| Tool | When to Use |
|---|---|
Bash (test runner) | Run npm test, pytest, go test, or equivalent to get fast per-file feedback during RED-GREEN cycles. |
Write / Edit | Write failing test first (RED), then write minimal production code (GREEN), then clean up (REFACTOR). |
mcp__plugin_oh-my-claudecode_t__lsp_diagnostics | Catch type errors and missing imports immediately after edits. |
mcp__plugin_oh-my-claudecode_t__lsp_diagnostics_directory | Run full project diagnostics before marking a cycle done. |
Grep | Find existing tests for the same module before writing a new one — avoid duplication. |
Glob | Locate test files matching the module under development. |
Typical cycle:
Write to the test file.Bash to confirm the failure.Edit the production file.mcp__plugin_oh-my-claudecode_t__lsp_diagnostics on both files.Edit production code, confirm tests stay green.TDD provides a tight feedback loop that keeps implementation anchored to behavior. Without it, code grows inward — implementation details proliferate, coupling increases, and tests (when written at all) fight the code instead of guiding it.
The OMP-specific variant emphasizes vertical slices via tracer bullets rather than horizontal layering. Horizontal TDD (write all tests for Layer A, then all for Layer B) produces integration gaps: every layer compiles in isolation but the system fails end-to-end. Vertical TDD traces one complete path from user input to data storage before moving to the next slice, delivering working software incrementally.
Deep modules are preferred because they minimize the interface surface you must test, making each test more powerful and easier to reason about. A large, well-hidden implementation behind a small interface is easier to verify, easier to refactor, and cheaper to change.
A test for a user-authentication module:
// auth.test.ts
import { authenticate } from './auth';
describe('authenticate', () => {
it('returns a session token when credentials are valid', async () => {
const token = await authenticate({ username: 'alice', password: 'secret123' });
expect(token).toMatch(/^[a-z0-9]{32}$/);
});
it('throws AuthError when password is incorrect', async () => {
await expect(
authenticate({ username: 'alice', password: 'wrong' })
).rejects.toThrow('Invalid credentials');
});
});
Notice: tests describe what the function returns and when it throws — not how it works internally.
// auth.ts
import crypto from 'crypto';
export class AuthError extends Error {
constructor(message: string) {
super(message);
this.name = 'AuthError';
}
}
const VALID_CREDENTIALS = { alice: 'secret123' };
export async function authenticate({
username,
password,
}: {
username: string;
password: string;
}): Promise<string> {
const expected = VALID_CREDENTIALS[username];
if (!expected || expected !== password) {
throw new AuthError('Invalid credentials');
}
return crypto.randomBytes(16).toString('hex');
}
This is the minimum code to make both tests pass. It is not the final architecture — it is the seed. The test survives refactoring because it tests behavior, not implementation.
The first green pass exposes that the credentials check and token generation are interleaved. Refactor to a deep module:
// credentials.ts (deep module — small interface, large hidden implementation)
const STORE = new Map<string, string>(); // currently in-memory; swap to DB later
export async function checkCredentials(username: string, password: string): Promise<boolean> {
const stored = STORE.get(username);
return stored !== undefined && stored === password;
}
export async function addCredential(username: string, password: string): Promise<void> {
// future: hash before storing
STORE.set(username, password);
}
The authenticate function becomes a thin, shallow orchestrator that delegates to deep modules. Tests for authenticate do not change — they only test the public interface.
Slice 1 — Register and retrieve a user
// user.test.ts
it('stores a user and retrieves them by id', async () => {
const id = await registerUser({ username: 'bob', password: 'pw' });
const user = await getUser(id);
expect(user.username).toBe('bob');
});
Write minimal code: in-memory store, registerUser writes to it, getUser reads back.
Slice 2 — Reject duplicate usernames
it('throws DuplicateError when username is already taken', async () => {
await registerUser({ username: 'bob', password: 'pw' });
await expect(
registerUser({ username: 'bob', password: 'pw2' })
).rejects.toThrow('Duplicate username');
});
Add the check to the existing registerUser — no new classes, no abstraction layers yet.
Slice 3 — Persist to database
Swap the in-memory store for a real DB adapter. Tests pass unchanged because the interface is the same.
Horizontal TDD would have written all in-memory tests, then all DB tests, then all business-logic tests — producing a large integration gap before any slice works end-to-end. Vertical TDD delivered a working registration flow after Slice 1.
lsp_diagnostics_directory reports zero errors.Run through these before marking a TDD cycle complete:
Is this test focused on a PUBLIC interface, not internals?
Test authenticate(params) — not authenticate._tokenCache or any private state.
Does the test describe BEHAVIOR, not implementation?
"returns a session token when credentials are valid" is behavior.
"calls crypto.randomBytes" is implementation — avoid it.
Will this test SURVIVE refactoring?
If you refactor credentials.ts internals, does this test still pass?
If not, the test is coupled to internals — rewrite it.
Is the code I wrote the MINIMAL amount to pass? No early optimization. No generalized abstractions. No extra parameters. Write what makes the test pass, nothing more.
Is this slice vertical — does it span from input to storage? If you only added a DB call without connecting it to the public API, the slice is incomplete.
Before declaring a TDD skill session complete:
npm test (or equivalent) runs green with zero failures.lsp_diagnostics_directory reports zero errors across the modified package.TODO, HACK, debugger, or console.log statements remain in production or test code.