Software Testing: Test-Driven Development
A blog series about software testing wouldn’t be complete without discussing Test-Driven Development, or TDD. In this blog post I want to give an overview of what TDD is. Furthermore, we will discuss a closely related technique called Acceptance Test-Driven Development (ATDD).
Let’s assume that we need to implement a function in software. Furthermore, let’s assume that we are given clear criteria what the function should output for each respective input. In this scenario, we can start by creating a set of tests: call the yet-to-be-implemented function with the various inputs and compare the actual outputs with expected outputs.
In TDD, the tests are written before the actual code itself.
After writing the tests, we can run the tests. However, at this stage the tests will fail because the function is not yet implemented. Now, we can start writing the implementation. As we create the implementation, we can run the tests in an iterative loop: we get quick feedback for our implementation as we can keep on constantly running the tests. Once the tests pass, the implementation is done.
The basic stages in TDD:
- Write tests
- Run tests
- Write implementation
- Repeat stages 2-3 until tests pass
Ideally, using TDD, implementing the function will be easier, because we already have tests to verify our solution against. Furthermore, using TDD, our solution might be simpler and less over-engineered, since we only solve the minimum criteria to make all the tests pass. Lastly, using TDD, we don’t need to write the tests after creating the implementation as we already wrote them beforehand.
Benefits of TDD (non-exhaustive list):
- Writing implementation is easier as tests are available.
- Implementation is less over-engineered as only required criteria is fulfilled.
- No need to write tests afterwards.
However, our example might have been a bit too simple and not very realistic. In the real world, our acceptance criteria is rarely well defined in advance. Likewise, if we don’t know what technologies are going to be involved in the implementation, are we really going to create the simplest test cases?
In general, TDD is best utilized for rather small scopes. For example, if you need to create the implementation for a data structure, you might know well in advance what the needed operations are and how they should behave – even if you don’t yet know exactly what the implementation will be like. In contrast, for larger scopes there may be more unknowns and now TDD becomes tedious and thus inefficient.
TDD works best when you target a small scope.
However, Acceptance Test-Driven Development, or ATDD, is slightly different. While TDD can be considered developer-centric, ATDD involves more stakeholders such as customers and product owners. Essentially, the difference between TDD and ATDD is semantic.
In ATDD, you start by defining product requirements as you normally would do. However, before starting the actual development, you write tests based on the requirements. Note that in contrast to conventional TDD, with ATDD you involve more parties in creating and approving the used tests. The tests become a way to acknowledge the requirements.
With ATDD everyone knows when requirements are fulfilled.
Like with TDD, after writing the tests, in ATDD you start creating the product implementation. As you already have the tests created, you can work in an iterative loop where you run the tests and see if your implementation works. As long as it doesn’t work, you know to still keep on iterating and improving the implementation.
The basic stages in ATDD:
- Define requirements
- Write tests based on the requirements
- Design the implementation
- Write the implementation
- Repeat stage 4 until tests pass
In this blog post we discussed Test-Driven Development. We discussed the benefits, but also why TDD may be hard to implement in the real world. Furthermore, we also discussed ATDD and why it can be powerful tool.
Latest from the blog
Software Testing: Conclusion
Software Testing: Reproducible Tests
Software Testing: Design Code with Testing in Mind
Software Testing: Infrastructure Testing
Software Testing: Characteristics Testing
Software Testing: Load Testing