No history yet

Red-Green-Refactor Mastery

The Core Cycle

Test-Driven Development (TDD) revolves around a short, repeatable loop. This isn't just a process; it's a rhythm that guides how you design and build software. It consists of three distinct phases: Red, Green, and Refactor. By cycling through these states, you create a safety net of tests that allows you to build and modify your code with confidence.

TDD proceeds in cycles which are usually called “red, green, refactor.”

This cycle turns testing from a verification step at the end into a design activity at the beginning. Instead of writing code and then figuring out how to test it, you write a test that describes what your code should do, and then you write the code to fulfill that description.

Phase 1: Red

The cycle always begins in the Red phase. Your goal is to write a single, small, automated test for a tiny piece of functionality you haven't implemented yet. The test should fail. In fact, it must fail, and for the right reason: the code doesn't exist or doesn't work as expected. This failing test is a clear, executable specification for what you're about to build.

A red test proves that the test itself works. If it can't fail, it can't be trusted to pass.

Let's say we need a function to calculate the factorial of a number. We'll start with the simplest case: the factorial of 0, which is 1. We write a test for that.

// Test for factorial of 0
// This test should fail when we first run it.
test('factorial of 0 is 1', () => {
  expect(factorial(0)).toBe(1);
});

When you run this, the test runner will report an error—probably a ReferenceError because the factorial function doesn't even exist. This is our red light. It's a signal to stop and write some code.

Phase 2: Green

In the Green phase, your objective is simple: make the failing test pass. The key is to write the absolute minimum amount of code required. Don't worry about elegance, efficiency, or future requirements. Just get to green. This focus on a singular goal keeps the process moving quickly.

To make our factorial(0) test pass, we could write this:

// The simplest possible code to pass the test
function factorial(n) {
  return 1;
}

It's not a complete factorial function, but it satisfies our test. When we run our test suite now, the test passes. We have a green light. This confirms that our new code meets the specific requirement defined by the test we just wrote.

Phase 3: Refactor

With a passing test, you now have a safety net. The Refactor phase is where you clean up your code. You can improve its internal structure, remove duplication, enhance readability, and pay down any technical debt you incurred while rushing to get to Green. The goal is to make the code better without changing its external behavior. Your test suite is what guarantees this; as long as all tests remain green, your refactoring is safe.

Our current factorial function is simple, so there isn't much to refactor yet. But this step is crucial. It's the moment you ensure your code remains clean and maintainable as it grows. The discipline is to refactor only when the light is green.

Red: Think about what you want. Green: Make it happen. Refactor: Make it right.

To evolve our function, we use a technique called triangulation. We'll add another test that forces a more general solution. Let's test for factorial(1).

// A new test that will fail
test('factorial of 1 is 1', () => {
  expect(factorial(1)).toBe(1);
});

Our existing code (return 1;) happens to make this test pass. So, we need another. How about factorial(2)?

// This will definitely fail (Red)
test('factorial of 2 is 2', () => {
  expect(factorial(2)).toBe(2);
});

This test fails. We're back in the Red phase. Now, to get to Green, we are forced to implement a more general solution.

// A more general solution to make all tests pass (Green)
function factorial(n) {
  if (n === 0) {
    return 1;
  }
  return n * factorial(n - 1); // A recursive solution
}

We run all tests. They all pass. We're Green again. Now we can enter the Refactor phase. Is this code clean? Is it easy to understand? For this simple example, it's pretty good. We can now confidently add another test and repeat the cycle, letting the tests drive our design forward one small, verifiable step at a time.

Ready to check your understanding of the core loop?

Quiz Questions 1/6

What are the three phases of the TDD cycle, in the correct order?

Quiz Questions 2/6

What is the primary goal during the 'Red' phase of the TDD cycle?

This disciplined cycle is the engine of TDD. It encourages simple designs, provides a safety net for change, and builds a comprehensive suite of regression tests as a natural byproduct of development.