Search Knowledge

© 2026 LIBREUNI PROJECT

Test-Driven Development (TDD)

Test-Driven Development (TDD)

Test-Driven Development (TDD) is a software development process where you write a test before you write the actual production code. It is a reversal of the traditional development process. TDD is not just a testing technique; it is a design process that ensures your code is testable, modular, and fulfills requirements.

The Red-Green-Refactor Cycle

The core of TDD is a simple repetitive cycle:

  1. 🔴 Red: Write a small, failing test for a new piece of functionality. The test should not even compile initially (if applicable) or should fail upon execution because the implementation doesn’t exist.
  2. 🟢 Green: Write the minimum amount of code necessary to make the test pass. Don’t worry about elegant code at this stage; just get it working.
  3. 🔵 Refactor: Clean up the code while ensuring all tests still pass. This includes removing duplication, improving naming, and ensuring the design is sound.

The Three Laws of TDD

Robert C. Martin (Uncle Bob) popularized three rules for TDD:

  1. You are not allowed to write any production code unless it is to make a failing unit test pass.
  2. You are not allowed to write any more of a unit test than is sufficient to fail; and compilation failures are failures.
  3. You are not allowed to write any more production code than is sufficient to pass the one failing unit test.

Benefits of TDD

  • Reduced Debugging Time: Since you test every small change, you know exactly where the bug is if a test fails.
  • Cleaner Architecture: TDD forces you to think about interfaces first, leading to more modularized code.
  • Documentation: The test suite becomes a living specification of the system.
  • Elimination of Fear: You can make large changes or refactors with the confidence that the tests will catch any breaks.

TDD and The Testing Pyramid

TDD is most effective when applied at the base of the “Testing Pyramid.” The pyramid suggests:

  • Unit Tests (Base): Largest number of tests. Fast and cheap.
  • Integration Tests (Middle): Fewer tests. Slower, testing interactions.
  • UI/E2E Tests (Top): Fewest tests. Very slow and brittle.

By focusing TDD on unit tests, you build a solid foundation of correctness that allows you to move faster.

ATDD: Acceptance Test-Driven Development

ATDD is an extension of TDD where the entire team (Business, Dev, QA) collaborates to define acceptance tests before development starts. These tests are written from the perspective of the user and define the “Definition of Done.” ATDD aligns closely with BDD (Behavior-Driven Development).

Example: TDD Workflow in TypeScript (Jest)

Imagine we are building a FizzBuzz function.

Phase 1: Red (Test for ‘1’)

// fizzbuzz.test.ts
import { fizzBuzz } from './fizzbuzz';

test('returns "1" for 1', () => {
  expect(fizzBuzz(1)).toBe("1");
});

This fails because fizzBuzz is not defined.

Phase 2: Green

// fizzbuzz.ts
export function fizzBuzz(n: number): string {
  return "1"; // Minimal code to pass
}

The test passes.

Phase 3: Red (Test for ‘3’)

test('returns "Fizz" for 3', () => {
  expect(fizzBuzz(3)).toBe("Fizz");
});

This fails because it returns “1”.

Phase 4: Green

export function fizzBuzz(n: number): string {
  if (n === 3) return "Fizz";
  return "1";
}

Phase 5: Refactor

As we add more tests (for 5, 15, etc.), the implementation will grow.

export function fizzBuzz(n: number): string {
  if (n % 15 === 0) return "FizzBuzz";
  if (n % 3 === 0) return "Fizz";
  if (n % 5 === 0) return "Buzz";
  return n.toString();
}

By following TDD, we ensure that every line of code is justified by a test case.