Test-Driven Development
SkillDev toolsImplement a change using test-driven development with RSpec. Guides the specify-encode-fulfill workflow.
Use Test-Driven Development in Claude, ChatGPT or Ahel Desktop
Free. Sign in, add Test-Driven Development and connect your AI. About a minute.
Also: Claude Code · Cursor · Codex
Then ask your AI: use the Test-Driven Development skill
Details
Instructions available. Your AI can read the instructions. Execution depends on the setup they require.
Account requirements not reviewed. Check the skill instructions before use; ahel provides instructions and does not run this skill.
No other account needed.
Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.
What this skill tells your AI
The instructions your AI receives, as published by dilawar/scripts in skills/tdd/SKILL.md and read by ahel’s review.
Initial Specification
$ARGUMENTS
The Specify-Encode-Fulfill Loop
I use something called "specify-encode-fulfill":
- Specify: Come up with the specifications for what you want to build
- Encode: Encode those specifications as automated tests (executable specifications)
- Fulfill: Write the code to fulfill the specifications
At a finer grain:
- Write a list of the specifications within scope of the current TDD session
- Encode one item in the list as an automated test
- Change the code just barely enough to make the current test failure go away. Avoid "speculative coding" - if we write more code than necessary to make the current test failure go away, we risk having code never exercised by any test
- Optionally refactor, but not before committing the behavior change. Never mix behavior changes with refactoring
- Until the list is empty, go back to #2
This follows Kent Beck's Canon TDD.
Clarifying Specifications
Before writing tests, follow this loop:
- Repeat my specifications back to me in your own words
- Ask me to confirm your articulation is correct or explain how it's wrong
- If confirmed, proceed to writing tests; otherwise use my response and go back to step 1
Specifications should take the form: "under scenario A, X happens; under scenario B, Y happens".
For guidance on designing good specifications, see test-design.md.
Translating Specifications into Tests
Each scenario should map to an RSpec example group. If the specification is, for example "when a test suite run's status is 'passed', its label says 'Passed'", then the test should look like this:
describe "#label" do
context "when status is 'passed'" do
it "returns 'Passed'" do
test_suite_run = TestSuiteRun.new(status: "passed")
expect(test_suite_run.label).to eq("Passed")
end
end
end
Here's an example of a BAD way to write such a test:
describe "#label" do
it "returns the correct value" do
test_suite_run = TestSuiteRun.new(status: "passed")
expect(test_suite_run.label).to eq("Passed")
end
end
Of course it returns the "correct" value. What else could we ever want from our code? Never assert that a behavior "works correctly" or "works properly" or "handles" certain scenario. What we want to specify in every scenario is what the correct behavior is.
Workflow
- I invoke /tdd with a draft specification
- After back-and-forth, we agree on "final" specifications
- See if we need to "clean the kitchen before we make dinner". Refer to the "Cleaning the Kitchen" section below.
- You write just one test (per Canon TDD)
- Show me the test and ask for approval before continuing
- Write the application code, show it to me, and ask for approval before committing. Refer to the "Fulfilling Test Specifications" section below.
- I provide a new specification and we start over from step 2
Cleaning the Kitchen
Before you write a test, picture the test you're going to write and where you're going to put it. Does the conceptual framework of this new behavior we're about to add slot tidily into the conceptual framework of the area of the code where we'll be adding it? If not, is there a reconceptualizing of the current behavior that could be done in order to make the ending result more conceptually elegant? If such a reconceptualizing is called for, suggest it to the user. If the user approves, abandon the current change, get to a clean working state, and, on a new branch, perform a refactoring. "Clean the kitchen before you make dinner." Then pause and consult the user and we'll begin again.
Fulfilling Test Specifications
When writing the application code to fulfill a certain specification, write ONLY ENOUGH CODE to make the current test failure go away. Never use "defensive coding". Defensive coding is almost always just speculative coding, which means code that's added without justification or feedback. Once you've written the test, invoke a separate subagent with the /test-design-review command to scrutinize your test code. Then invoke another subagent with the /software-design-review command to scrutinize your application code.
Don't Be Sloppy
This kind of thinking is bad:
That failure is pre-existing (unrelated to our change — it's in send_results). Our 6 new + existing specs pass. Want me to commit and push?
We don't make dinner in a dirty kitchen. If we discover a pre-existing failure, the right move is to pause, stash our changes, fix the pre-existing failure, then resume.
Signals
- GitHub stars
- 29
- Forks
- 21
- Last commit
- Sep 2026
Advanced
- Item type
- skill
- Key
tdd-dilawar- Source
- github.com/dilawar/scripts
Related picks
Skill · posthog
The pick for Rubyvigilante-issue-implementation-on-ruby
Skill · aliengiraffe
The pick for Rubyteach
Skill · mattpocock
More in Dev toolsimplement
Skill · mattpocock
More in Dev toolscaveman
Skill · juliusbrussee
More in Dev toolsponytail
Skill · dietrichgebert
More in Dev tools