skills/codingcossack/test-driven-development/SKILL.md
Red-green-refactor development methodology requiring verified test coverage. Use for feature implementation, bugfixes, refactoring, or any behavior changes where tests must prove correctness.
npx skillsauth add aiskillstore/marketplace test-driven-developmentInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Write test first. Watch it fail. Write minimal code to pass. Refactor.
Core principle: If you didn't watch the test fail, you don't know if it tests the right thing.
NO BEHAVIOR-CHANGING PRODUCTION CODE WITHOUT A FAILING TEST FIRST
Wrote code before test? Delete it completely. Implement fresh from tests.
Refactoring is exempt: The refactor step changes structure, not behavior. Tests stay green throughout. No new failing test required.
RED ──► Verify Fail ──► GREEN ──► Verify Pass ──► REFACTOR ──► Verify Pass ──► Next RED
│ │ │
▼ ▼ ▼
Wrong failure? Still failing? Broke tests?
Fix test, retry Fix code, retry Fix, retry
Write one minimal test for one behavior.
Good example:
test('retries failed operations 3 times', async () => {
let attempts = 0;
const operation = async () => {
attempts++;
if (attempts < 3) throw new Error('fail');
return 'success';
};
const result = await retryOperation(operation);
expect(result).toBe('success');
expect(attempts).toBe(3);
});
Clear name, tests real behavior, asserts observable outcome
Bad example:
test('retry works', async () => {
const mock = jest.fn()
.mockRejectedValueOnce(new Error())
.mockRejectedValueOnce(new Error())
.mockResolvedValueOnce('success');
await retryOperation(mock);
expect(mock).toHaveBeenCalledTimes(3);
});
Vague name, asserts only call count without verifying outcome, tests mock mechanics not behavior
Requirements: One behavior. Clear name. Real code (mocks only if unavoidable).
MANDATORY. Never skip.
npm test path/to/test.test.ts
Test must go red for the right reason. Acceptable RED states:
Not acceptable: Runtime setup errors, import failures, environment issues.
Test passes immediately? You're testing existing behavior—fix test. Test errors for wrong reason? Fix error, re-run until it fails correctly.
Write simplest code to pass the test.
Good example:
async function retryOperation<T>(fn: () => Promise<T>): Promise<T> {
for (let i = 0; i < 3; i++) {
try {
return await fn();
} catch (e) {
if (i === 2) throw e;
}
}
throw new Error('unreachable');
}
Just enough to pass
Bad example:
async function retryOperation<T>(
fn: () => Promise<T>,
options?: { maxRetries?: number; backoff?: 'linear' | 'exponential'; }
): Promise<T> { /* YAGNI */ }
Over-engineered beyond test requirements
Write only what the test demands. No extra features, no "improvements."
MANDATORY.
npm test path/to/test.test.ts
Confirm: Test passes. All other tests still pass. Output pristine (no errors, warnings).
Test fails? Fix code, not test. Other tests fail? Fix now before continuing.
After green only: Remove duplication. Improve names. Extract helpers.
Keep tests green throughout. Add no new behavior.
Next failing test for next behavior.
Minimal: One thing per test. "and" in name? Split it. ❌ test('validates email and domain and whitespace')
Clear: Name describes behavior. ❌ test('test1')
Shows intent: Demonstrates desired API usage, not implementation details.
Bug: Empty email accepted
RED:
test('rejects empty email', async () => {
const result = await submitForm({ email: '' });
expect(result.error).toBe('Email required');
});
Verify RED:
$ npm test
FAIL: expected 'Email required', got undefined
GREEN:
function submitForm(data: FormData) {
if (!data.email?.trim()) {
return { error: 'Email required' };
}
// ...
}
Verify GREEN:
$ npm test
PASS
REFACTOR: Extract validation helper if pattern repeats.
Any of these means delete code and restart with TDD:
| Problem | Solution | |---------|----------| | Don't know how to test | Write the API you wish existed. Write assertion first. | | Test too complicated | Design too complicated. Simplify the interface. | | Must mock everything | Code too coupled. Introduce dependency injection. | | Test setup huge | Extract helpers. Still complex? Simplify design. |
The Iron Law ("delete and restart") applies to new code you wrote without tests. For inherited code with no tests, use characterization tests:
Characterization tests lock down existing behavior so you can refactor safely. They're the on-ramp, not a permanent state.
Tests must be deterministic. Ban these in unit tests:
vi.useFakeTimers(), jest.useFakeTimers())Flaky test? Fix or delete. Flaky tests erode trust in the entire suite.
Bug found? Write failing test reproducing it first. Then follow TDD cycle. Test proves fix and prevents regression.
Before diving into the cycle, spend 2 minutes listing the next 3-10 tests you expect to write. This prevents local-optimum design where early tests paint you into a corner.
Example test list for a retry function:
Work through the list in order. Add/remove tests as you learn.
When writing tests involving mocks, dependencies, or test utilities: See references/testing-anti-patterns.md for common pitfalls including testing mock behavior and adding test-only methods to production classes.
For detailed rebuttals to common objections ("I'll test after", "deleting work is wasteful", "TDD is dogmatic"): See references/tdd-philosophy.md
Production code exists → test existed first and failed first
Otherwise → not TDD
development
Apple Human Interface Guidelines for content display components. Use this skill when the user asks about charts component, collection view, image view, web view, color well, image well, activity view, lockup, data visualization, content display, displaying images, rendering web content, color pickers, or presenting collections of items in Apple apps. Also use when the user says how should I display charts, what's the best way to show images, should I use a web view, how do I build a grid of items, what component shows media, or how do I present a share sheet. Cross-references: hig-foundations for color/typography/accessibility, hig-patterns for data visualization patterns, hig-components-layout for structural containers, hig-platforms for platform-specific component behavior.
tools
Automate HelpDesk tasks via Rube MCP (Composio): list tickets, manage views, use canned responses, and configure custom fields. Always search tools first for current schemas.
testing
Expert Haskell engineer specializing in advanced type systems, pure functional design, and high-reliability software. Use PROACTIVELY for type-level programming, concurrency, and architecture guidance.
tools
GraphQL gives clients exactly the data they need - no more, no less. One endpoint, typed schema, introspection. But the flexibility that makes it powerful also makes it dangerous. Without proper controls, clients can craft queries that bring down your server. This skill covers schema design, resolvers, DataLoader for N+1 prevention, federation for microservices, and client integration with Apollo/urql. Key insight: GraphQL is a contract. The schema is the API documentation. Design it carefully.