This post was originally published on DZone here.
What Is Test-Driven Development?
Test-driven development (TDD) refers to an approach in software development geared towards reducing errors and improving flexibility when designing applications. It is a process that encourages a quick, rapid, and fearless testing development style. This approach to programming enables tests to drive the design of the whole system.
The following set of rules (designed originally by Robert C. Martin, or “Uncle Bob”) defines the best practice guidelines for TDD:
- You are not allowed to write any production code unless it is to make a failing unit test pass.
- You are not allowed to write any more of a unit test than is sufficient to fail, and compilation failures are failures.
- You are not allowed to write any more production code than is sufficient to pass the one failing unit test.
Abiding by these rules will provide IT teams with a tight feedback loop between production code and test code.
As highlighted earlier, the aim is to first write a unit test before any production code is started. The aim is to develop the system (and high-quality code) gradually through an ongoing sequence of tiny test improvements. By rule two, you obviously can’t write much of the unit test itself other than is required for the test code to fail. This approach is generally referred to as red, green, and refactor. Write a small failing test (red), and then help it to pass by writing good production code (green). Then, the final step is to refunction the code (refactor) before it’s possible to restart the process. (Callaway and Hunt, 2018.)
Does TDD Fit in the Agile, Lean, and DevOps Spaces?
Where TDD Fits in the Agile and Lean Space
For projects already affiliated with Agile methodologies of software development, implementing TDD is fairly simple. It is ideally suited for building complex projects with many features to clearly defined requirements — an integral ideal of Agile. TDD will help to improve the efficiency of the development process and encourage teams to produce products with reliable scalability and desired quality. The TDD approach is highly applicable to greenfield projects being developed from scratch.
If the project is the development of a basic app or building on proof-of-concept, TDD is not necessarily justifiable. It can admittedly be time-consuming. On the other hand, it’s not realistic to buy a car that hasn’t been through any quality control (QC), so there’s no real argument for leaving QC out of a software development process, either.
In Lean Software Development, the integral aim is to reduce waste in projects. The application of test-driven development reinforces this core aspect. Furthermore, TDD is useful for building quality into the process by eliminating defects from the very beginning rather than at the end of the system where most QC takes place. Though building quality in is a central Lean principle, it isn’t always easy. Implementing TDD will help overcome this hurdle.
TDD in the DevOps Space
In DevOps, high-performing IT teams have turned to TDD to produce high-quality end products. While the DevOps umbrella can cover many workflows and ideas, here are the ideal practices you can incorporate with TDD involved.
DevOps processes advocate breaking down heavy complex projects for everyone involved in the project. All stakeholders are continuously involved in the whole software development process and DevOps promotes a flow of work that reduces any negative impact on anyone else down the value stream. In this case, TDD can be implemented by first creating the initial tests and running them manually (as suggested in The DevOps Handbook) and once they’re written, they can be added to an automation pipeline to save time in the future.
Bring End-User Feedback Front and Center
The DevOps methodology entails fostering a culture of collaboration, especially with the ultimate end-users. Involving the end-users early in the process by constantly getting their feedback is crucial to make the final product meet their expectations. Teams can use TDD to engage in initial tests needed to help design the end-product in accordance with a thorough understanding of the needs of end-users. Having this knowledge will help to develop useful initial tests.
Adoption of an Enterprise Test Management Tool (ETMT)
A robust ETMT tool like Jenkins, XL TestView, or Selenium can provide crucial support in handling all the necessary tests in TDD. Such a tool guarantees that everyone receives all the necessary information at all stages of development — a truly DevOps concept. ETMTs help those new to the TDD concept, as well as the entire team, receive all the benefits of a TDD environment. (Kim et al., 2016.)
Pros and Cons of Using Test-Driven Development
Advantages of TDD
- Coding becomes modular when executing small tests at a time. TDD assists in understanding, learning, and internalizing the fundamentals of good modular design.
- Inherent code structural problems surface much earlier and can be addressed swiftly. Thus, TDD facilitates good architecture.
- TDD simplifies the maintenance and refactoring of code. It also facilitates clarity during the implementation stage. In addition, it provides a safety cushion during the refactoring step.
- TDD offers developers greater insight into the perspective of the end-users’ POVs.
- TDD helps reduce the defects rate. The identification of design or requirement issues happens right at the start when it is early enough to be fixed without having any impact down the value stream.
- Implementing TDD encourages the use of an automated regression test suite. There is no need to spend time testing implementation code after writing unit tests — it’s built into the process from the start.
Disadvantages of TDD
- TDD slows development at the beginning, as it necessitates ample time and effort to write unit tests.
- It does not necessarily guarantee unit test quality. Focus goes on metrics such as code coverage, which does not actually assess quality. (DevOps approaches such as peer reviews and paired programming can mitigate some of this step.)
- In complicated scenarios, test cases can be tough to calculate and create.
- If there is a rapid change of design, changing tests is also necessary, which can slow development. These changes can result in a lot of time wasted; e.g. writing tests for features that may get dropped later. (Good product design and planning can address the issue of going off-project.)
Tips on How to Implement Test-Driven Development
1. Ensure TDD Is the Right Fit for Your Project
TDD can provide a much-needed boost of confidence to teams, but it can also be a false source of security. First, ascertain that TDD is the appropriate approach for your project. TDD is ideal for products where you know the exact specifications (or have time to generate these) or where you’re trying to stabilize an existing one — in which case, this process can serve as the best route to a quality product.
2. Decide the Inputs and the Outputs
Start the process from a high level of determining your eventual product goal. Decide the inputs necessary to achieve that product goal and then outline the necessary outputs/feature steps to reach it. Consider the overall function of the product, as well as the data that the function will require to run and the results that this function will return. This step is an integral parameter to consider before writing any code or test.
3. Select the Function Signature
Having established what data goes in and the outcome to expect, it’s now vital to choose the function signature. These are the parameters that the function takes and the expectations that the function will return a certain result. This step is similar to writing code without TDD. Generally, in code writing, deciding the parameters and return value is a necessary step.
4. Decide on One Small Aspect of Functionality
Figure out the most straightforward possible behavior that draws your team closer to the end goal. Using simple behavior can assist to determine the smallest amount of code for each step of your process. This code can bring the function closer to becoming a working end-product.
5. Implement Test
It is worth noting that all previous steps were similar to programming without TDD. The significant difference is the focus on implementing the function. This is opposed to how the functions behave concerning certain conditions. Your focus is to test how the functions behave at every step. When you begin testing behavior at every step, the testing gets easier the closer you get to the end-product.
6. Implement Code
You should practically write the slightest amount of code to pass each unit test as outlined at the beginning of this article. To advance on the same function, continue with these small steps, repeating from Step 4. TDD gets a lot easier to achieve as your team gets used to the process, and it helps in eliminating tedious and helpless coding after a product launch.
To make your organization successful, adapt to the changing environments and address project improvement through TDD. Spending time on the process early on will boost productivity, output, and market value much later down the line. Test-driven development is the ideal approach to achieving application flexibility, extensibility, and maintainability.
- Callaway, J. and Hunt, C. (2018). Practical test-driven development using C# 7. Packt Publishing.
- Kim, G., Humble, J., Debois, P., and Willis, J. (2016). The DevOps Handbook. Portland: IT Revolution Press.
- Martin, R. (2005). The Three Rules Of TDD. Available here. (Accessed 14 Jun. 2018).
Caylent offers DevOps-as-a-Service to high growth companies looking for help with microservices, containers, cloud infrastructure, and CI/CD deployments. Our managed and consulting services are a more cost-effective option than hiring in-house and we scale as your team and company grow. Check out some of the use cases and learn how we work with clients by visiting our DevOps-as-a-Service offering.