Test-Driven Development
Writing a failing test first, then the code that makes it pass
Overview
Test-Driven Development (TDD) flips the usual order: you write a test before the code it checks. The rhythm is Red, Green, Refactor — write a failing test (red), write just enough code to pass it (green), then clean up the code while tests stay green. This keeps designs simple and guarantees every line is covered by a test.
Syntax / Usage
Start with a test for behavior that doesn't exist yet. It fails because the function isn't written, which proves the test is real.
// Step 1 (Red): write the test first
const { slugify } = require("./slugify");
test("converts a title to a url slug", () => {
expect(slugify("Hello World")).toBe("hello-world");
});
// Step 2 (Green): minimal code to pass
function slugify(text) {
return text.toLowerCase().replace(/\s+/g, "-");
}
module.exports = { slugify };
Run the test after each step. Only refactor once it is green.
Examples
Adding a new requirement by writing the next failing test:
test("removes punctuation", () => {
expect(slugify("Hello, World!")).toBe("hello-world");
});
// Step 3 (Refactor): extend code to satisfy the new test
function slugify(text) {
return text
.toLowerCase()
.replace(/[^\w\s]/g, "")
.trim()
.replace(/\s+/g, "-");
}
module.exports = { slugify };
Common Mistakes
- Writing the implementation first, then backfilling tests, which isn't TDD
- Writing a huge test that forces you to build everything at once
- Skipping the refactor step, leaving messy code behind passing tests
- Not watching the test fail first, so a broken test can silently pass
- Testing internal helpers instead of the behavior you actually want
See Also
testing-unit-tests testing-fundamentals testing-mocking