3 min read

Test Driven Development (TDD)

Test Driven Development (TDD) is a software development process where you write the test before you write the actual code. It relies on the repetition of a very short development cycle.

1. The Red-Green-Refactor Cycle

The core of TDD is a simple 3-step loop:

  1. Red: Write a failing test for a small piece of functionality. It fails because the feature doesn't exist yet.
  2. Green: Write just enough code to make the test pass. Do not worry about code quality or elegance here; just get it to pass.
  3. Refactor: Clean up the code while keeping the test passing. Remove duplication, improve names, and apply design patterns.

2. The Three Laws of TDD

Robert C. Martin (Uncle Bob) defines three rules:

  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.

3. Example (JavaScript)

Step 1: Red (The Test)

// math.test.js
const { add } = require('./math');

test('adds 1 + 2 to equal 3', () => {
  expect(add(1, 2)).toBe(3);
});
// Fails: 'add' is not defined.

Step 2: Green (The Code)

// math.js
function add(a, b) {
  return a + b;
}
module.exports = { add };
// Test passes!

Step 3: Refactor (In this simple example, there isn't much to refactor, but if we had hardcoded return 3, we would now change it to a + b).

4. Benefits

  • High Test Coverage: Since you write tests for everything, you end up with near 100% coverage by default.
  • Better Design: Writing the test first forces you to think about the API design (how the code will be used) before implementation details.
  • Confidence: You can refactor aggressively because you have a safety net of tests.
  • Documentation: The tests serve as living documentation of how the system behaves.

5. Common Pitfalls

  • Testing Implementation Details: Tests should check behavior, not internal state. If you test internals, refactoring becomes impossible without breaking tests.
  • Skipping Refactoring: If you only do Red-Green, you accumulate technical debt.
  • Slow Tests: If tests are slow, developers won't run them frequently, defeating the purpose of the tight feedback loop.

6. Behavior Driven Development (BDD)

BDD is an evolution of TDD that focuses on the behavior of the application from the end-user's perspective. It uses a natural language syntax (Gherkin) to describe tests.

  • Focus: "What" the system should do, rather than "how" it is implemented.
  • Language: Uses Given, When, Then syntax.
  • Tools: Cucumber, SpecFlow.

Example (Gherkin)

Feature: User Login

  Scenario: Successful Login
    Given the user is on the login page
    When the user enters valid credentials
    Then the user should be redirected to the dashboard

programming/software-testing-basics programming/refactoring-techniques programming/agile-methodology