Back to Blog
Lesson 25 of the Software Testing & Debugging: Testing & Debugging Foundations (QA) course
TestingAugust 12, 20264 min read

The Red-Green-Refactor Cycle: Master the TDD Workflow

Learn the Red-Green-Refactor cycle to master TDD. Improve your code quality, reduce bugs, and gain confidence with this essential engineering workflow.

TDDtest driven developmentrefactoringcoding workflowunit testing
Close-up of AI-assisted coding with menu options for debugging and problem-solving.

Previously in this course, we covered First Steps into Unit Testing and Asserting Expected Outcomes. While those lessons taught you how to write and assert tests, they didn't dictate when to write them. In this lesson, we introduce the Red-Green-Refactor cycle, the core workflow that turns testing from a chore into a design tool.

The TDD Loop from First Principles

Test-Driven Development (TDD) is not just about testing; it is a coding workflow that uses tests to drive the design of your implementation. By writing tests before the actual code, you force yourself to consider the API and the requirements before implementation details distract you.

The cycle consists of three distinct phases:

  1. Red: Write a test for a tiny bit of functionality that doesn't exist yet. Run it and watch it fail. This confirms your test is actually testing something and that the requirement isn't met.
  2. Green: Write the bare minimum code required to make the test pass. Do not write "perfect" code here; just satisfy the assertion.
  3. Refactor: Look at the code you just wrote. Clean up duplication, improve naming, and simplify logic while the tests stay green.

Worked Example: Building a Calculator Function

Let’s advance our project by implementing a sum function for a new utility module. We will follow the loop strictly.

Phase 1: Red

We want a function that adds two numbers. We create a test file calculator.test.js and write the test first.

JAVASCRIPT
// calculator.test.js
const { sum } = require(CE9178">'./calculator');

test(CE9178">'adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3);
});

Running this fails immediately with ReferenceError: sum is not defined. This is our Red state. We have confirmed the test fails because the code is missing.

Phase 2: Green

Now, we write only what is needed to pass the test.

JAVASCRIPT
// calculator.js
function sum(a, b) {
  return a + b;
}
module.exports = { sum };

Run the test again. It passes! We are in the Green state.

Phase 3: Refactor

Currently, our code is simple, but imagine we had added complex logic or hardcoded values. We would now review it to ensure it follows clean code principles. If we had written a more complex implementation to pass the test, we would now strip away the "ugly" parts, confident that our test acts as a safety net.

Hands-on Exercise: Implement a Subtract Function

Now it's your turn. Apply the Red-Green-Refactor cycle to add a subtract function to your project:

  1. Red: Add a test in calculator.test.js that expects subtract(5, 3) to equal 2. Run it; ensure it fails.
  2. Green: Implement subtract in calculator.js to make the test pass.
  3. Refactor: Check if your function naming or structure aligns with your sum function.

Common Pitfalls

  • Skipping the "Red" Phase: If you write the code first, you aren't doing TDD; you're just writing tests for existing code. If your test passes immediately, you haven't proven it can fail, which is a common source of "false sense of security."
  • Over-engineering during Green: Don't try to build a generic mathLibrary engine when you only need sum(a, b). Stick to the simplest thing that works.
  • Refactoring without Tests: Refactoring is only safe when you have a green test suite. Never change code structure without confirming your tests still pass afterward.

FAQ

Does TDD take longer? Initially, yes. However, it significantly reduces the time spent on The Scientific Method of Debugging later, as you catch issues as you write them.

What if I can't write a test first? Sometimes you're working with legacy code. In those cases, read about Developer productivity: Why I stopped writing clean code first to understand how to balance momentum with quality.

Recap

The Red-Green-Refactor cycle provides a rhythm to your development. You move from the chaos of a missing feature to the safety of a passing test, and finally to the clarity of clean, refactored code. By adhering to this loop, you ensure every line of code you write is tested and necessary.

Up next: We will dive deeper into Writing Failing Unit Tests First, where we will treat requirements as test specifications.

Similar Posts