agentsclimarketplace

Test first

Skill ccpowerpack/claude-code-skills/skills/test-first

15 gate-style skills that give Claude Code the discipline of a senior engineer — observable preconditions, not advice. Survives model downgrades.

Install
npx -y skills add ccpowerpack/claude-code-skills --skill test-first

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Enforced test-driven development — write the failing test, run it to prove it fails, then implement until green. Use when the user asks to build a feature "with TDD", "test-first", or wants high-confidence implementation of well-specified behavior.

SKILL.md

9.2 KB, as published. Nobody here has run it

Test First

You are implementing with strict TDD. The discipline exists for one reason: a test you never saw fail proves nothing. A green suite where every test was born green is indistinguishable from a suite of tautologies.

The loop

For each behavior, in order:

1. Red (gate: you must observe the failure)

  • Write ONE test for the next smallest behavior. Name it after the behavior, not the method: test_expired_token_is_rejected, not test_validate_2.
  • Run it. Paste the failure output.
  • Verify it fails for the RIGHT reason — an assertion failure on the behavior, not an import error, syntax error, or missing fixture. ModuleNotFoundError is not a red test; it's a broken test. Fix the scaffolding first and re-run until the failure is the assertion you wrote.
  • If the test passes before you've implemented anything, the test is vacuous. Rewrite it.

2. Green (gate: passing output for this test AND the suite)

  • Write the minimum implementation that makes this test pass. Resist implementing ahead of the tests — if you catch yourself writing a branch no test forces, delete it or write the test first.
  • Run the test. Paste the passing output.
  • Run the full relevant suite — you may not proceed with other tests newly broken.

3. Refactor

  • With green tests as a safety net: remove duplication, improve names, extract structure that has earned its existence.
  • Run the suite again after refactoring. Refactor step ends green or gets reverted.

Repeat until the feature's behaviors are covered.

Choosing the next test

Before starting, list the planned test cases as a checklist and keep it updated so the user sees progress. Order them to maximize learning per test:

Behavior classWhen to test itExample test name
Happy path, simplest inputFirst — forces the API shape into existencetest_first_request_passes_through
Core invariantSecond — the reason the feature existstest_duplicate_key_returns_cached_response
Boundary: empty / zero / absentAfter the invariant holdstest_request_without_key_is_not_deduplicated
Boundary: max / overflow / unicodeSame pass as other boundariestest_key_longer_than_255_chars_is_rejected
Error path: invalid inputAfter boundaries — needs the validation seamtest_malformed_key_returns_400_not_500
Error path: dependency failureLate — needs the fake/mock seam to existtest_store_outage_fails_open_not_closed
Concurrency / interactionLast — hardest to write, needs stable internalstest_concurrent_same_key_executes_handler_once
Time-dependent behaviorLast, with a fake clock (see below)test_key_expires_after_ttl

Skip a row only if it genuinely doesn't apply — say so in the checklist rather than silently omitting it. If mid-loop you discover a behavior the plan missed, add it to the checklist visibly before writing its test — the checklist is a living contract, not a prologue.

Prove-the-red commands per ecosystem

Run ONE test fast; the tight loop dies if every red costs a full-suite run.

EcosystemRun one testNotes
pytestpytest tests/test_idem.py::test_key_expires_after_ttl -x -q-k "expires" for substring match; -x stops at first failure
jestnpx jest idem.test.ts -t "expires after ttl"-t matches the test title; add --watch for the loop
vitestnpx vitest run idem.test.ts -t "expires after ttl"vitest without run starts watch mode — ideal for red-green
go testgo test ./middleware/ -run 'TestKeyExpiresAfterTTL$' -v -count=1-count=1 defeats the test cache — cached PASS is not evidence
cargocargo test key_expires_after_ttl -- --exact --nocapturesubstring match by default; --exact pins one test
JUnit (Maven)mvn test -Dtest='IdempotencyTest#keyExpiresAfterTtl' -qGradle: ./gradlew test --tests 'IdempotencyTest.keyExpiresAfterTtl'

Whatever the ecosystem: the red run and the green run must be the SAME command, pasted both times. If the runner has a test cache (go, gradle, turbo/nx-wrapped jest), disable it for the red/green pair — a cache hit reporting PASS is not an observation.

Worked example — idempotency-key middleware, three cycles

Feature: HTTP middleware that deduplicates POSTs carrying an Idempotency-Key header.

Cycle 1 — happy path. Test: first request with a key passes through to the handler.

$ pytest tests/test_idem.py::test_first_request_passes_through -x -q
E   AttributeError: module 'app.middleware' has no attribute 'IdempotencyMiddleware'

Wrong-reason failure — scaffolding, not behavior. Create the empty class, re-run:

E   assert handler.call_count == 1
E    +  where 0 = handler.call_count
1 failed in 0.04s

Right reason. Implement pass-through. 1 passed in 0.05s. Suite green.

Cycle 2 — the invariant. Test: second request with the same key returns the stored response and does NOT call the handler again.

$ pytest tests/test_idem.py::test_duplicate_key_returns_cached_response -x -q
E   assert handler.call_count == 1
E    +  where 2 = handler.call_count
1 failed in 0.05s

Implement: store (status, body) by key after the first call; short-circuit on hit. 2 passed in 0.06s.

Cycle 3 — boundary. Test: request without the header is never deduplicated.

E   assert handler.call_count == 2
E    +  where 1 = handler.call_count

Interesting red: the naive implementation cached under key None, so two keyless requests collided. The test caught a real bug before it shipped. Fix: bypass the store entirely when the header is absent. 3 passed in 0.07s.

Refactor. Extract ResponseStore from the middleware body — it has earned its existence: cycle 2 created it, cycle 3 gave it a second rule. Re-run: 3 passed in 0.07s.

Async and time-dependent code — no sleeps

sleep(0.5) in a test is a flake with a countdown timer. Two rules:

  1. Time is an injected dependency. The code under test reads a clock you control; the test advances it deterministically.
  2. Wait for events, not durations. If the test must wait for async work, wait on a completion signal (future, channel, waitFor on an observable condition) — never a fixed delay.
EcosystemFake clockAsync sync point
Pythonfreezegun.freeze_time() / time-machinepytest-asyncio: await the coroutine directly; drive the loop, don't sleep
JS/TS (jest)jest.useFakeTimers() + jest.advanceTimersByTime(60_000)await Promise.resolve() to flush microtasks; jest.runAllTimersAsync()
JS/TS (vitest)vi.useFakeTimers() + vi.advanceTimersByTime()await vi.runAllTimersAsync()
Goinject a Clock interface (jonboulle/clockwork, benbjohnson/clock)channels / sync.WaitGroup; t.Deadline()-bounded selects, never time.Sleep
Rust (tokio)#[tokio::test(start_paused = true)] + tokio::time::advance(Duration::from_secs(60)).awaitawait the task handle; paused time auto-advances past sleeps
JVMjava.time.Clock injected; Clock.fixed(...) in testsAwaitility await().until(...) polls a condition, not a duration

For the idempotency example: test_key_expires_after_ttl injects a fake clock, stores at T, advances the clock by ttl + 1s, asserts the handler runs again. Zero wall-clock waiting, zero flake surface.

If the codebase has existing sync utilities or a clock abstraction, use those — matching house convention beats importing a new library.

Where real time genuinely cannot be faked (integration test against a real broker, subprocess startup), poll a condition with a deadline — await().atMost(5, SECONDS).until(queueIsEmpty) — never a fixed delay. The deadline is a failure bound, not an expected duration: the test passes the instant the condition holds.

Test quality bar

  • Each test asserts one behavior; multiple asserts are fine if they describe one outcome.
  • No sleeping/polling-by-duration anywhere — see the table above.
  • Use real objects where cheap; mock only at process/network boundaries.
  • The test must read as documentation: given / when / then visible in its structure.
  • Test names form a spec when listed: pytest --collect-only -q output should read like the feature's behavior list.

Anti-patterns (hard no)

  • Writing the implementation first "to understand the problem", then backfilling tests.
  • Batch-writing 10 tests then implementing everything at once — that's test-after with extra steps.
  • Marking a test skipped/todo to get to green.
  • Weakening an assertion to make it pass. If the assertion was wrong, say so explicitly and justify the change.
  • Claiming red without pasted failure output, or pasting a red that failed on imports/fixtures rather than the assertion.
  • sleep() in any test, including "just 100ms to be safe".

Output contract

Finish with: the checklist of behaviors covered (each linked to its test name), final full-suite run output pasted, and any behavior you deliberately left untested (with reason).

Gives 2 of the 12 instructions most tdd skills give

Counted across 439 of the 443 authors here whose files we hold, read 2026-08-06

  • write minimal code to pass the testhere, and in 302 of 439, across 218 files
  • write a failing test firstin 176 of 439, across 112 files
  • refactor code only after tests passin 171 of 439, across 101 files
  • watch the test fail before writing codein 142 of 439, across 93 files
  • test one behavior per testin 106 of 439, across 44 files
  • refactor code while keeping tests greenin 99 of 439, across 86 files
  • delete code written before testsin 98 of 439, across 54 files
  • run tests after each refactor stepin 85 of 439, across 54 files
  • Use real code instead of mocks unless unavoidablein 64 of 439, across 21 files
  • confirm the test fails for the right reasonhere, and in 64 of 439, across 60 files
  • reproduce bugs with a test before fixingin 53 of 439, across 36 files
  • write tests before implementationin 48 of 439, across 39 files

Said here and by no other author read

  • maintain a checklist of planned behaviors
  • paste the failure output
  • paste the passing test output
  • disable test caches when running tests
  • inject fake clocks for time-dependent code
  • wait for events not durations in tests

Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.