Test-Driven Development (TDD)

Test Driven Development (TDD) is a software development approach that emphasizes writing tests before writing the actual code.

I created this post as a personal exploration of the concept of a “Test-Driven Development” in the context of testing. My goal is to gain a deeper understanding of how (TDD) works and its significance in software development.

The TDD Cycle

TDD follows a specific cycle often referred to as the “Red-Green-Refactor” cycle.

100%
graph LR; A[Write a Test] -->|Failing Test| B[Write Code] B -->|Pass Test| C[Refactor Code] C -->|Loop Back| A

The iterative cycle of Test Driven Development.

In this example, we will demonstrate the TDD process through a simple use case: creating a function that adds two numbers.

# test_calculator.py

import unittest
from calculator import add

class TestCalculator(unittest.TestCase):

    def test_add(self):
        self.assertEqual(add(1, 2), 3)  # This test expects the result of add(1, 2) to be 3
        
if __name__ == '__main__':
    unittest.main()

Red-Green-Refactor Explained

  1. Red: Write a test that defines a desired improvement or new function. The test should fail because the functionality is not yet implemented.
  2. Green: Write the minimum amount of code necessary to pass the test. At this stage, the aim is only to get the test to pass, not to implement any kind of perfection or optimization.
  3. Refactor: Once the test is passing, improve the code without changing its functionality, focusing on code quality, readability, and maintainability.
The cycle of TDD ensures that development is tightly aligned with requirements and encourages ongoing improvement of code quality.

Python Code Example for the Cycle

# calculator.py

def add(a, b):
    return a + b  # Implement minimum code to pass the test

Once this change is made, you would then run the tests again, observing that they pass (Green stage). Afterward, you might refactor the code (if needed) while ensuring that the test remains successful.

Improved Code Quality

By writing tests first, TDD promotes better design and cleaner code.

Benefits of Code Quality

Impact of TDD on Code Quality

100%
graph TD; A[Write Tests] -->|Encourages Modular Design| B[Reduced Duplication] B -->|Improved Maintainability| C[Higher Overall Quality]

How TDD influences code design.

Let’s use a scenario where we need to handle a list of values and return their sums.

# test_calculator.py (extended)

def test_add_multiple_numbers(self):
    self.assertEqual(add(1, 2, 3), 6)  # Testing multiple inputs
    
# Modify the add function accordingly
def add(*args):
    return sum(args)  # Now can add any number of arguments

This encourages developers to create a cleaner, more modular solution that can handle various input scenarios.

Enhanced Collaboration

TDD fosters collaboration between technical and non-technical team members by creating clear expectations.

Clear Communication

By leveraging test cases as a form of documentation, teams can use TDD to:

Tip: Utilizing tests as documentation helps to bridge gaps between developers and stakeholders, allowing for effective collaboration.

Using the previous add function as an example, tests serve as documentation for what the expected behavior is regarding different input scenarios. Imagine a scenario where contributors can see:

def test_add_with_negative_numbers(self):
    self.assertEqual(add(-1, -1), -2)  # Clear expectations set for input 

This communicates to the team how negative numbers should be handled, facilitating discussions and clarifying misunderstandings.

Faster Feedback Loops

TDD enables rapid validation of assumptions and desired behaviors in the code.

Benefits of Faster Feedback

100%
graph TD; A[Develop Small Features] -->|Run Tests| B[Immediate Feedback] B -->|Refine Code| C[Iterate Development] C -->|Enhance Quality| D[Final Product]

The rapid feedback process in TDD.

When implementing changes iteratively, you could include tests for edge cases:

# test_calculator.py (extended)

def test_add_large_numbers(self):
    self.assertEqual(add(1000000, 2000000), 3000000)  # Test for large inputs

This supports fast feedback, allowing developers to identify any areas that may cause issues immediately.

Continuous Integration (CI)

TDD fits seamlessly into a Continuous Integration/Continuous Deployment (CI/CD) pipeline.

Integration with CI/CD

Integrating TDD into CI/CD practices amplifies the benefits of automated testing and enhances overall software quality assurance.

Python Code Example in CI Context

Your test suite could be triggered during CI processes using configuration files (like GitHub Actions or Travis CI). Here’s a sample command you might include in your CI script:

# .github/workflows/python-package.yml

name: Python package

on: [push]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
    - name: Install Python
      uses: actions/setup-python@v2
      with:
        python-version: '3.x'
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt
    - name: Run tests
      run: |
        python -m unittest discover -s tests

This setup ensures your tests run on every push, maintaining confidence in code changes.

Evolving Codebase

As requirements change or evolve, maintaining the quality of the codebase becomes easier with TDD.

Adapting to Changes

When changes are needed, TDD allows developers to:

100%
graph TD; A[Change Requirements] -->|Update Tests| B[Modify Code] B -->|Run Tests| C[Verify Functionality] C -->|Ensure Stability| D[Improved Codebase]

How TDD helps adapt to evolving requirements.

Consider a scenario where the requirements change to include additional functionality:

# test_calculator.py (extended)

def test_subtract(self):
    self.assertEqual(subtract(5, 2), 3)  # New functionality needed
    
# Implement the new function accordingly
def subtract(a, b):
    return a - b

Conclusion

Test Driven Development transforms the way software is developed by intertwining testing with the development process.

Adopting TDD enables teams to maintain agility in their development processes, respond effectively to changing requirements, and produce software that meets user needs more reliably.

Good luck :D

Further Reading/Resources

Stay Updated

Subscribe my newsletter

© 2025 Pavlin

Instagram GitHub