Red-Green-Refactor: Mastering Test-Driven Development in Python

Overview of Test-Driven Development

(TDD) flips the traditional coding process on its head. Instead of building features and then checking for bugs, you write the test first. This forces you to define the requirements and interface of your code before a single line of implementation exists. Originally popularized by
Kent Beck
in his book
Test Driven Development by Example
, this methodology creates a safety net that ensures your code does exactly what it is supposed to do from the start.

Prerequisites

To follow this guide, you should have a solid grasp of

basics, including classes and methods. Familiarity with basic math operations and a general understanding of how software modules interact is helpful.

Key Libraries & Tools

  • unittest
    : The built-in
    Python
    library for creating and running tests. It provides a framework for organizing test cases and making assertions.
  • Python
    Standard Library
    : Used for basic data types like floats and integers, though decimals are often preferred for financial accuracy.

Code Walkthrough

The TDD cycle follows the Red-Green-Refactor pattern.

The Red Phase

First, we write a test for a method that doesn't exist yet. In our example, we want to compute an employee's payout. We create a test case that expects a specific float value.

Red-Green-Refactor: Mastering Test-Driven Development in Python
Test-Driven Development In Python // The Power of Red-Green-Refactor
import unittest
from employee import Employee

class TestEmployee(unittest.TestCase):
    def setUp(self):
        self.test_emp = Employee(name="Alice", pay_rate=100.0, hours_worked=10)

    def test_payout_with_commission(self):
        self.test_emp.contracts_landed = 10
        # Expecting 1000 (base) + 1000 (commission) = 2000
        self.assertAlmostEqual(self.test_emp.compute_payout(), 2000.0)

Running this now results in a failure (Red) because compute_payout is not implemented.

The Green Phase

Now, write the simplest possible code to make the test pass. Avoid over-engineering; just satisfy the assertion.

def compute_payout(self):
    payout = self.pay_rate * self.hours_worked
    if self.has_commission:
        payout += self.commission_rate * self.contracts_landed
    return payout

Running the tests again should yield a success (Green).

The Refactor Phase

With passing tests, you can safely clean up the code. You might split a generic employer_cost variable into specific categories like office_costs and 401k_costs. Because you have a test harness, you know immediately if your cleanup broke the logic.

Syntax Notes

  • setUp: This method runs before every single test. Use it to create a fresh "test fixture" (like an
    Python
    instance) so tests stay isolated.
  • assertAlmostEqual: Essential when comparing floats. It accounts for the tiny rounding errors inherent in floating-point math.

Practical Examples

TDD is vital in financial systems where calculating tax or payroll requires extreme precision. It is also excellent for API development, where defining the input and output (the interface) first prevents scope creep.

Tips & Gotchas

  1. Isolate Tests: Never use global instances. If Test A modifies an object that Test B relies on, you'll face nightmare debugging scenarios.
  2. Don't Test Built-ins: You don't need to test if
    Python
    can assign a variable. Focus on your custom logic.
  3. Use Fixed Values: Always compare your code's output against a hard-coded, known result rather than a copy of the logic inside the test file.
Red-Green-Refactor: Mastering Test-Driven Development in Python

Fancy watching it?

Watch the full video and context

3 min read