TDD stands for Test-Driven Development. Contrary to what I mostly hear from others that it's a testing approach, No, it is not a testing approach. Rather, it's a development practice where tests are used to determine not only the correctness but also the completeness of the code. We often hear about TDD as something where tests are written first before the code. Which is partially correct because it's not only it. Test-driven development (TDD) is a software development methodology in which tests are written for a new piece of code before the code itself is written. The tests are designed to fail initially because if it doesn't, it means it's an invalid test, or the test is clearly not working. But as the implementation code is developed, it is written specifically just to pass the tests. By the time the implementation code is complete, it means it is already tested.
If you are familiar with developer assessment platforms like HackerRank, Codility, and Coder byte, or if you have attended algorithmic hackathons like Facebook Hacker Cup and Google CodeJam, the way developers work in these environments is that they write their code and then press a button. That button press runs a series of tests in ~2 seconds and comes back with a report that says your code has passed or not. TDD is actually very similar, except that the tests are also written by the same developer who's trying to solve the problem.
Another difference is that in TDD, the developers don't write an entire 1000-line test suite first. Instead, it actually follows the following cycle.
- Write tests that check for very specific behavior.
- The tests should fail because there has yet to be an implementation code written.
- Write just enough code to make the tests pass.
- Then refactor your code until you are satisfied with your design.
- Go back to step 1.
This approach to development has gained popularity in recent years because it can help to ensure that code is well-designed, easy to maintain, and free of defects.
One of the key advantages of TDD is that it forces developers to think about the desired behavior of their code before they start writing it. This helps to ensure that the code is well-designed and easy to understand. It also helps to prevent developers from writing code that is difficult to maintain or modify in the future. It prevents the developer from writing codes that are not necessary. Developers often call this "over-design." In other words, developers are unknowingly being forced to write good-quality code.
Another advantage of TDD is that it can help to catch defects early in the development process. Because tests are written before the code, developers can identify and fix defects as soon as they are introduced. This can save time and effort in the long run, as it is much easier to fix a defect early on than it is to track it down and fix it later on.
The Challenges
TDD is not without its challenges, however. There are also common mistakes the teams make when dealing with TDD.
One common challenge is that writing tests can be time-consuming, and it can be tempting for developers to skip this step in the interest of saving time. However, skipping the testing step can lead to defects and other problems down the line. In the end, as the quality degrades, the developers will end up spending more time fixing other problems that are not captured by tests, and this time is usually more than the time they save from not writing the needed tests.
Another challenge with TDD is that it can be difficult for developers who are new to the methodology to know how to write effective tests. Writing tests that are comprehensive enough to cover all possible scenarios can be a daunting task, and it can take time and experience to develop the skills needed to write effective tests.
Automating UI tests as part of the TDD approach may only sometimes work because UI tests typically take some time to run, and they are often fragile. Another thing is that because these tests are very visual and are more than just behavior checking, it is often left out of the TDD cycle. However, there are attempts to automate visual testing by Gojko Adzic that he describes in this talk.
The test activity done by the QA/testing team may seem redundant. It is true that because the code is already tested, then there is no need to perform manual tests execution of the same tests written by the developers. However, there are tests that cannot be covered or are very difficult to implement in the TDD approach, and this includes integration tests. The TDD tests are mainly unit tests, and any interaction with external systems is usually mocked. One way to solve this challenge is by bringing the testers closer to the developers. Let the testers define which behavior needs to be tested. The developer will then tell the testers which behaviors are already covered by the TDD tests and that the testers can focus on these other tests. This problem is less prevalent in Microservices Architecture, though. That's because each service is isolated and are independent from each other, the need for integration tests is lesser.
Despite these challenges, many developers and organizations have found that the benefits of TDD outweigh the challenges. By forcing developers to think about the desired behavior of their code and by catching defects early in the development process, TDD can help to ensure that code is of high quality and easy to maintain. As a result, TDD has become an increasingly popular approach to software development.