All topics
Testingintermediate

Test-Driven Development (TDD)

A development workflow where a failing test is written before the implementation code, following a strict red-green-refactor cycle.

Write a failing test, make it pass, then improve the code.

TDD is like a sculptor who first describes precisely, in words, exactly what the finished statue's specific pose and expression should look like (the failing test) before touching the marble, then carves only enough to match that exact description (minimal implementation), and only afterward smooths and polishes the surface (refactor) — confident that the carved shape's core measurements already match the original written description, checked and rechecked, throughout the smoothing process.

Key Concepts

1
    red      write a test for behaviour that does not exist -- it fails
    green    write the simplest code that passes it
    refactor improve the design, with the test protecting you
2
The order is the point. Writing the test first forces you to decide what the code should do before deciding how — which usually produces a simpler interface, because you are the first caller.
3
A worked cycle.
    // red
    it('returns 0 for an empty cart', () => {
      expect(total([])).toBe(0);
    });
4
    // green -- deliberately minimal
    const total = () => 0;
5
    // red again
    it('sums the item prices', () => {
      expect(total([{ price: 10 }, { price: 5 }])).toBe(15);
    });
6
    // green
    const total = (items) => items.reduce((s, i) => s + i.price, 0);
7
Writing return 0 looks absurd and is the discipline: it proves the test fails for the right reason, and each test drives one more piece of behaviour.
return 0
8
What it actually buys, beyond the tests themselves.
9
A test suite that is complete by construction. Every line exists because a test demanded it.
10
Refactoring with confidence. The tests were written against the behaviour, so they still pass after the implementation changes.
11
Better interfaces. Code that is awkward to test is usually awkward to use, and you find that out immediately rather than at the third call site.
12
Where it does not fit. Exploratory work where you do not yet know what you are building. UI layout. Code whose correctness is visual. And anything where the test would be a restatement of the implementation.
13
The honest position. Strict TDD on everything is rare in practice. Writing the test first for logic with clear inputs and outputs — pricing, validation, parsing, state transitions — is where it pays, and that is a large share of the code that matters.
14
And the failure mode. Tests written after the fact tend to assert what the code does, including its bugs. Writing the test first is what stops that, which is the argument even for people who do not follow the cycle rigidly.
15
What the interviewer is probing.1. "Describe the cycle." Probing: the process. Stalls: "Write tests first." Moves up: red, green, refactor — a failing test, the simplest code to pass it, then improve the design with the test as a safety net.
16
2. "What does TDD give you beyond coverage?" Probing: the real benefit. Stalls: "Confidence." Moves up: design pressure — code that is hard to test is usually badly coupled, so the difficulty is feedback.
17
3. "When would you not use it?" Probing: honesty. Stalls: "Always use it." Moves up: exploratory work where the design is unknown, or UI layout where the assertion is visual.
18
4. "Why write the failing test first?" Probing: the point. Stalls: "Convention." Moves up: a test that has never failed proves nothing — you have not seen it detect the absence of the behaviour.