Test-Driven Development skill for Codex

See it fail for the right reason, then pass. Codex stops writing code and matching tests in one pass. It agrees a test list with you, predicts each failure before running it, writes the least code to pass, and refactors only on a green suite. It avoids watch modes that hang the shell, keeps doubles at real boundaries, and finishes with a mutation spot-check that proves the tests can catch a broken line.

  • Engineering Workflow
  • $test-driven-development
  • 153 lines
  • 2 files
  • ~1,650 tokens
  • MIT
  • Updated

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- test-driven-development

Installs to ~/.agents/skills/test-driven-development/ for every project on this machine.

104-line POSIX script. No sudo, no telemetry. Read install.sh

Use it in Codex

Start a prompt with $test-driven-development, or pick it from /skills. You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.

What Codex reads before loading it

Builds features and fixes test-first with a strict red-green-refactor loop - a written test list, one failing test at a time that fails for the stated reason, the smallest code that passes, then refactoring on green - using the repo's own runner (pytest, Vitest, Jest, go test, cargo test, JUnit). Use when the user asks for TDD or test-first work, when behaviour is specified by examples, when fixing a bug that needs a regression test, or when an agent keeps writing code and tests together that pass trivially.

Use it when

  • The user asks for TDD or test-first development
  • Behaviour is specified as examples or acceptance criteria
  • A bug fix needs a regression test that provably fails first
  • Agent-written tests keep passing trivially

Use something else when

How to check the result

Each cycle shows a red run with the predicted failure before the green run, the full suite passes, and deliberately breaking one new line makes at least one test fail.

The mistake it prevents

Editing the assertion to match what the code returns, which turns a failing test into a recording of the bug.

~/.agents/skills/test-driven-development/SKILL.md

--- name: test-driven-development description: Builds features and fixes test-first with a strict red-green-refactor loop - a written test list, one failing test at a time that fails for the… license: MIT ---

Every behaviour in the final diff was demanded by a test that was observed failing for the right reason before the code existed. The suite stays green between cycles, each cycle is small enough to explain in one sentence, and refactoring happens only on green.

Why this matters more for an agent

An agent that writes implementation and tests in one pass tends to produce tests that mirror the code, including its bugs. Seeing the test fail first is the only proof the test can detect anything. Treat the red step as mandatory evidence, not ceremony.

Setup

  1. Read AGENTS.md and the test config (pyproject.toml, vitest.config.*, jest.config.*, go.mod, Cargo.toml) to find the runner and the command for a single test. Match existing test file locations and naming.
  2. Confirm the suite is green before starting. If it is red, report the existing failures and agree with the user whether to proceed; never start TDD on a red base.
  3. Pick the fastest focused command. You will run it dozens of times:
pytest tests/test_pricing.py -x -q -k "bulk"         # -x stops at first failure
npx vitest run src/pricing.test.ts -t "bulk"           # run mode; watch mode blocks the shell
npx jest src/pricing.test.ts -t "bulk"
go test ./pricing -run 'TestBulk' -count=1
cargo test pricing::bulk
./gradlew test --tests 'com.acme.PricingTest.bulk*'

Never use watch mode (vitest without run, jest --watch) inside Codex; it never returns control.

The test list

Before any code, write the list of behaviours as one-line examples and show it to the user. It is the plan and the definition of done.

Bulk pricing
- [ ] 1 unit at list price -> 10.00
- [ ] 10 units get 5% off -> 95.00
- [ ] 100 units get 12% off -> 880.00
- [ ] discount tiers do not stack
- [ ] quantity 0 -> 0.00
- [ ] negative quantity -> ValueError
- [ ] result rounds half-even to cents

Order it: the simplest degenerate case first, then the main path, then edges and errors. New ideas that appear mid-cycle go on the list, not into the current cycle.

The cycle

Red

  • Write exactly one test for the next item. Name it after the behaviour, not the method: test_ten_units_get_five_percent_off.
  • Run it. It must fail, and fail for the reason you predicted. State the prediction before running (“fails with AssertionError 100.00 != 95.00”).
  • Wrong failure reason (import error, typo, fixture missing) means the test is not yet a test. Fix the scaffolding until the failure is the intended assertion.
  • A test that passes immediately either duplicates an earlier one or the behaviour already exists. Delete it or keep it as documentation, but say which.

Green

  • Write the least code that makes this test pass. Hard-coding is allowed when it is truly the least code; the next test will force generalisation.
  • Run the focused test, then the whole file or package. Everything green.
  • Do not touch tests in this step. If you feel the urge to edit an assertion to make it pass, stop: either the test was wrong (say why to the user) or the code is.

Refactor

  • Only on green. Remove duplication between code and tests, name things properly, extract helpers. Tests may be refactored too, one side at a time.
  • Re-run after every change. A red during refactor means revert that change, not debug it.
  • Commit at green when the repo workflow allows it, one cycle or a few related cycles per commit, so a bad step is cheap to undo.

Choosing the next test

SituationNext test
Implementation is hard-codedA second example that breaks the hard-code (triangulation)
Logic feels obviousWrite it directly, then continue (obvious implementation)
Stuck for more than two attemptsA smaller test: a sub-step or a helper’s behaviour
Error handlingThe specific exception type and message, not just “raises”
Many similar examplesParametrize once the shape is stable, not before

Test doubles

  • Prefer real collaborators that are fast and deterministic (pure functions, in-memory repositories). Mock only at boundaries you do not own: network, clock, randomness, filesystem when slow.
  • Inject time and randomness instead of patching globals: pass a clock or rng.
  • Assert on outcomes (return value, state, emitted message), not on which internal methods were called. Interaction assertions belong only at real boundaries.
  • In the Codex sandbox, network is often off. A test that needs the network is not a unit test; give it a fake or move it to integration tests.

Bug fixes, test-first

  1. Write a test that reproduces the reported bug through the public interface.
  2. Run it and see it fail with the reported symptom. If it passes, your understanding of the bug is wrong; go back to the reporter’s steps.
  3. Fix. Run the test, then the suite.
  4. Keep the test. Its name describes the behaviour, optionally with the issue number.

Hard rules

  • No production code without a failing test that requires it, except pure refactors on green.
  • Never weaken, skip (@pytest.mark.skip, it.skip, t.Skip) or delete a failing test to get to green without the user’s agreement.
  • One failing test at a time. Two reds at once means you cannot tell which change fixed what.
  • Do not assert on private internals to drive design; drive through the public API.
  • Do not mass-generate the test list as code up front. The list is prose; tests arrive one per cycle.

When TDD is the wrong tool

Spikes to learn an unknown API, UI layout tweaks, and throwaway scripts. Say so, spike without tests, then delete the spike and rebuild test-first if it becomes real code.

Verification

  • Every item on the test list is checked off or explicitly deferred with a reason.
  • Full suite green using the command from AGENTS.md.
  • Mutation spot-check: break one line of the new logic (flip a comparison, change a constant), run the tests, confirm at least one fails, revert. For a systematic version see mutation-testing.
  • No skipped or focused tests left behind: rg -n "\.only\(|\.skip\(|@pytest\.mark\.skip|t\.Skip\(" <test dirs>.

Report

### TDD summary: <feature>
Cycles: 7 (all observed red before green)
Test list: 7/7 done; deferred: <none | item + reason>
Files: src/pricing.py (+42), tests/test_pricing.py (+68)
Design notes: tier lookup extracted to `tier_for(quantity)` during refactor of cycle 4
Mutation spot-check: changing `>=` to `>` in tier_for fails 2 tests
Suite: `pytest -q` 214 passed

What the zip contains

SKILL.md plus the interface file below. Nothing runs on install.

agents/openai.yamlgenerated from this page

Gives the Codex app a display name, a one-line description and a starter prompt for this skill.

interface:
  display_name: "Test-Driven Development"
  short_description: "See it fail for the right reason, then pass"
  default_prompt: "$test-driven-development Implement the bulk pricing rules test-first, one test at a time"

About this skill

Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.

esc
Just launched

Codex Skills Pro is live

79 premium skills for Codex, each with scripts, references and an evidence report that proves the result.

Bought one by one$882.21
All 79 skills$49
94% off

That's $0.62 a skill, paid once.

Get all 79 for $49Or pick single skills from $4.99 →

Secure checkout by Creem