Skip to main content

Benefits of Test-Driven Development in DevOps Environments

Linkedin

In a previous blog post entitled The Importance of Testing in DevOps, we discussed why we need to establish a good testing strategy as part of our implementation of a DevOps model.

As we saw, unit testing forms the foundation level in our testing pyramid. In this blog post, we explain how using Test-Driven Development can help us achieve this solid testing foundation.

The traditional model is not conducive to writing unit tests

In traditional development models, unit testing is usually only written once the functionality has been developed. One of the typical problems we encounter is that, due to concerns over delivery times, this test writing phase is often dropped. Furthermore, if a lot of time has passed between writing the functionality and writing the tests, the task becomes much more time-consuming. Lastly, if we develop the functionality without keeping in mind that it will need to be tested later, we often end up with code that is extremely difficult, and sometimes impossible, to test.

The problem is compounded if the team writing the unit tests is not part of the team of programmers who wrote the code. This brings us to the first rule that we need to follow when writing unit tests:

The developers who write the code must also write the unit tests.

Test-Driven Development helps us avoid these problems

One development technique that helps us avoid these problems is Test-Driven Development (TDD). In a nutshell, TDD requires the tests in our code to be written before the code itself. 

Many people mistakenly think that TDD is a testing tool when in fact it is a software design and development technique that has the knock-on effect of generating numerous unit tests. The high degree of testing coverage is therefore not the main objective of TDD, but a side effect.

Other benefits of using TDD include the simplicity of the code generated, the fact that this code can always be tested, and the resulting executable documentation, which is always up-to-date because the tests themselves are used for this purpose. The number of errors is also drastically reduced, as is the time spent on development and maintenance over the long term.

Lastly, writing the tests before writing the code clearly defines when a task has been completed: when all the tests requiring the developed functionality run successfully (pass).

What is Test-Driven Development (TDD)?

The TDD process – often referred to as the TDD Mantra – is actually very simple, as the figure below shows.

test driven development

Red Phase

The Red phase always comes first. In this phase, based on a requirement, a user story or anything that describes the functionality, we write the test or tests to validate this functionality. It’s called the Red phase because red is the error colour used for failed tests. We always start with a failed test.

Green Phase

Once we have the failed test, we implement the simplest code that will make the failed test pass. This doesn’t need to be the most elegant or efficient code. The goal is to reach a safe and stable point by moving forward in small steps. This is where we follow the KISS principle: Keep it Simple, Stupid.

At this point, we can either move on to the Refactor phase, or return to the Red phase where we write a new test that breaks the implementation we’ve developed.

Speed is a key element in this process. Moving from the Red to the Green phase, and vice versa, has to be very fast, within just a few minutes. If it takes you hours to change phases, you’re doing it wrong.

Refactor Phase

Once we have written a series of tests, or just one test, we move on to the Refactor phase in which we improve the code without changing the functionality. A common mistake in this phase is changing the functionality instead of improving the design. For this phase, we strongly recommend following typical refactoring patterns.

By the end of this phase, we have clean, simple and efficient code.

If we repeat this cycle, we end up with code that can be tested and has passed the test, and with a design that is as simple as possible and has been developed in an incremental and iterative way.

Using the Ping Pong Pair Programming technique

Learning how to develop using Test-Driven Development can be very complicated because it often conflicts with some of our basic assumptions about development, assumptions such as the code having to be written before it can be tested and the testing phase coming after the development phase.

On this point, it is essential to remember that testing is an activity and not a phase of software development.

A technique that is particularly useful for improving development with TDD is the pair programming technique known as “ping pong”. Apart from being a fun practice, it provides all the benefits of pair programming, so it’s worth giving it a try.

Using this technique, the programming pair applies the following protocol:

  • One partner in the pair writes a failed test and passes it on to the other partner in the pair.
  • This partner writes the code that passes the test, performs some degree of refactoring, then writes a new failed test before passing control back to the first partner in the pair.
  • They repeat this cycle as if they were playing a game of ping pong.

The image bellow illustrates this process.

Ping Pong Pairing

What does this have to do with DevOps?

How does using Test-Driven Development benefit the implementation of a DevOps model?

As we mentioned at the beginning of this blog post, having a solid unit testing foundation is essential and a key enabler of continuous delivery. Using TDD gives us this foundation and lays the groundwork for our test automation strategy. This foundation improves the reliability of our code and increases confidence in our deployment pipeline. Furthermore, the TDD process itself forces us to take only small steps forward, which is key to the success of a DevOps implementation, as we saw in the blog post on using small batches.

Therefore, using TDD helps us significantly in our task of implementing a DevOps model.

In future posts, we’ll take a look at other testing strategies that can help us at other levels of our test automation pyramid.

Emiliano Sutil is a Project Manager at Xeridia