test-driven development
Let one failing example drive the next implementation.
Use this when You can describe the behavior before writing it.
without the term
“Before you build this, can you write some tests? Actually write them first and make sure they fail, then write the code, then make the tests pass. I don't want tests that just copy whatever the code does.”
with the term · How do you ask a model for test-driven development?
Use TDD for this behavior. Show the expected failing assertion, implement it, then rerun the relevant tests.
What is test-driven development?
TDD cycles through a failing test, enough implementation to pass, and cleanup while tests stay green.
Why does test-driven development matter when vibe coding?
A generated test can agree with generated code while both miss the requirement.
How do you do test-driven development?
Choose one observable example. Run red, green, then refactor. Check that red failed for the missing behavior.
What goes wrong with test-driven development?
A syntax error is mistaken for proof that the test detects the missing behavior.
Try a small example
A slug should trim spaces and collapse runs of whitespace. Save this as slug.test.mjs. Start with the deliberately incomplete implementation.
import test from 'node:test';
import assert from 'node:assert/strict';
const slug = (text) => text;
test('trims and joins words', () => {
assert.equal(slug(' Hello world '), 'hello-world');
});Run node --test slug.test.mjs. The assertion should fail with the unmodified input as the actual value. Replace the function body with text.trim().toLowerCase().replace(/\s+/g, "-") and rerun. It should pass. Then add another behavior before expanding the implementation. This one example is not a complete slug specification.
Before and after
An agent builds a URL slug function. The requirement is to trim surrounding spaces, lowercase the words, and join whitespace-separated words with a hyphen. Start with that observable behavior before asking for an implementation.
Before
A function that returns its input can look complete in a generated diff. A test that expects the unchanged input would also pass, while missing the requested behavior.
function before(text) {
return text;
}After
The assertion fixes the expected result independently of the implementation. Run it against the incomplete function to see the mismatch, add only the needed behavior, then rerun it before refactoring.
function after(text) {
return text.trim().toLowerCase().replace(/\s+/g, '-');
}Check the change
For the input ' Hello world ', the initial function returns the unchanged string; the revised function returns 'hello-world'. The comparison file checks that the first assertion fails and the revised examples pass. To experience the red step directly, remove the assert.throws wrapper and run that assertion before changing the function.
Save the runnable comparison, then run node --test tdd.mjs with Node.js 22.12 or later.
Download the complete teaching example: the bug, prompt, runnable code, expected result, limits, and sources, linked back to this page.
Show the assertions
test('the initial implementation misses the requirement', () => {
assert.throws(() => {
assert.equal(before(' Hello world '), 'hello-world');
}, { code: 'ERR_ASSERTION' });
});
test('the implementation satisfies the chosen examples', () => {
assert.equal(after(' Hello world '), 'hello-world');
assert.equal(after('One'), 'one');
assert.equal(after(''), '');
});When this is not enough
This demonstrates a small, specified behavior. It does not settle Unicode normalization, punctuation, uniqueness, or URL policy, and passing examples do not prove that the whole product is correct. Add each relevant rule deliberately. TDD includes refactoring after green; writing tests after the implementation is a different workflow.
The next decision
- minimal reproduction: For an existing bug, first capture the smallest input and expected outcome that reproduce it.
- refactoring: Once the behavior is green, improve the structure while keeping those checks passing.
Sources and verification
- Martin Fowler: Test Driven Development
The test, implementation, and refactoring cycle. The slug example is our own illustration.
- Node.js test runner
Running the downloadable comparison with node --test.
Sources checked . These are illustrative examples with automated checks. How we check content.
Did this help with your task?
After trying the prompt or check, tell us what happened. This optional response records only your choice and this term.