Test Driven Development 101

Table of Contents:

What is Test Driven Development (TDD)

TDD Development Cycle

Pro’s and Cons

Type’s of Testing

Conclusion

Introduction

Test Driven Development (TDD) is a development practice in which developers create a set of tests before implementing the code that they are going to test. The idea is that the developer writes tests around the expected results and requirements which they know in advance.If the tests pass after implementing the software then the developer can have confidence that their code is working as expected. On the other hand if any of the tests fails then it signals to the developer that something is wrong.

TDD Development Cycle

A common phrase you might see when talking about TDD is the “Red-Green-Refactor” cycle. This cycle is the key principal in TDD. This cycle refers to the three states that occur when developing using this practice.

Red State: When the developer initially writes tests that are going to fail because they are simply not implemented yet or there have been changes in the requirements

Green State: When the developer writes code so that all the tests pass which ensures that the requirements and expected behaviours are met

Refactor State: Once the tests have passed you can go back and clean up the previously written code in any which way you want, so that there is less duplication or readability etc.

The core concept behind TDD is that once you get to the refactor state you should have already had a working version of your code, so if in the process of refactoring you change a behaviour the tests will once again return back to the red state and the cycle continues.

Example

For example lets say we want to make a calculator program in python, and we know want that calculator to have a multiplication function. This is how that might look if we are using TDD

This example will be written in python and uses the doctest package for tests

First we will create our class with some examples that will tests if our implementation is correct

import doctest

class Calculator:
    """
    A simple calculator class that can multiply.

    Examples:
    >>> calc = Calculator()
    >>> calc.multiply(2, 3)
    6
    """

if __name__ == "__main__":
    doctest.testmod()

This code will fail because we haven’t implemented the multiply method yet but it showcases how we expect the method to behave. This is our Red State

We then move on to implementing the methods and update our file like so

import doctest

class Calculator:
    """
    A simple calculator class that can multiply.

    Examples:
    >>> calc = Calculator()
    >>> calc.multiply(2, 3)
    6
    """

    def multiply(self, a, b):
        product = 0
        for i in range(0, b):
            product += a
        return product

if __name__ == "__main__":
    doctest.testmod()

At this point the test we wrote is now passing so we know that the method we just implemented is working the way we intended it to work. This is our Green State

Now that we have a Green state we can move on to our Refactor State. If we change our multiply method fundamentally and alter its behaviour our original test will once again fail indicating that our refactor changed the behaviour, we want to refactor without changing any behaviour. We update our file like so.

import doctest

class Calculator:
    """
    A simple calculator class that can multiply.

    Examples:
    >>> calc = Calculator()
    >>> calc.multiply(2, 3)
    6
    """

    def multiply(self, a, b):
        return a * b

if __name__ == "__main__":
    doctest.testmod()

Our tests are still passing which means our refactor did not change any behaviour while we were able to optimize the code.

This is a trivial example of TDD and following this pattern if we wanted to add more methods like add and subtract we can follow similar steps and be confident that those changes don’t effect multiply as long as the multiply test is still passing

Pros and Cons

You might be thinking to yourself “wow TDD seems great, it ensures that the code does exactly what you want it to do why is this not the standard?”. For as great as a principle TDD is it also has its drawbacks, its up the developer to decide whether or not TDD fits their use case.

Pros

Cons

Types of Testing

Typically for TDD the type of tests written are unit tests, where we test a singular unit (i.e function,method,class). This way we can have confidence that the individual smaller parts of our entire system are working and if all the small parts work individually we either have a working system or an easier time debugging which area of the system is misbehaving. Unit tests are usually the go to when it comes to TDD.

End-to-end(E2E) tests are more concerned about how different parts of the entire system are connected. They test the cohesion of all parts of the system whether that includes the frontend,backend,database etc. They typically test how users will interact with the system and ensure that services are running correctly. E2E tests are more related to behaviour driven development(BDD) which is out of the scope of this article but there will be links at the bottom to learn more.

Conclusion

Test driven development is a methodology where you first write tests outlining how you want the code to work and then write the code to achieve that behaviour. The idea behind this concept is that there is an initial investment of time and complexity but it forces the developer to write better code that will be easier to maintain in the future and that the time saved in debugging and maintenance will offset the initial investment. However TDD is not a one-size-fits all and there are scenarios in which TDD can have more downsides than rewards.

Extra Resources

Videos: Jim Coplien and Bob Martin Debate TDD YOU DONT UNIT TEST??? Test-Driven Development // Fun TDD Introduction with JavaScript

Articles: What is Test Driven Development Test-driven development - IBM Garage Methodology

Popular Testing Frameworks: Cypress PyUnit doctests JUnit Jest

BDD: What is BDD? - Agile Alliance What is BDD? - Browser Stack TDD vs BDD