What we would like to focus on here are the positives, the arguments in favor of TDD.
Arguments in favor of TDD
Reduces the effort of manual testing
We already mentioned that we, as professionals, will not ship anything without first determining that it works. Throwing something over the wall to QA, to our users, or to the general public and hoping that it all works as expected just isn't how we do business. We will verify that our code and our applications work as expected. In the beginning, while the application is small and has little functionality, we can manually test everything we can think of. But, as the application grows in size and complexity, it just isn't feasible for developers or anyone else to manually test an entire application. It’s too time-consuming and costly to do this manually. We can save ourselves time and our clients and companies money by automating our testing. We can do so quite easily, from the beginning, through TDD.
Reduces bug count
As our application grows, so do our tests. Or shall we say, our test suite has grown, and by making our tests pass, our application has grown. As both have grown, we've covered the happy path (for example: 2 + 2 = 4) as well as potential failures (for example: 2 + banana = exception). If the method or function under test can accept an input parameter, there is a potential for failure. You can reduce the potential for unexpected behavior, bugs, and exceptions by writing code to guard against these scenarios. As you write tests to express potential failures, your production code will inherently become more robust and less prone to errors. If a bug does slip by and make it to QA, or even to a production environment, then it's easy enough to add a new test to cover the newly discovered defect.
The added benefit of approaching bugs in this fashion is that the same bug rarely crops up again at some later date, as the new tests guard against this. If the same bug does appear, you know that, while the same result has happened, the bug occurred in a new and different way. With the addition of another test to cover this new scenario, this will likely be the last time you see the same old bug.
Ensures some level of correctness
With a comprehensive suite of tests, you can demonstrate some level of correctness. At some point, someone somewhere will ask you whether you are done. How will you show that you have added the desired functionality to an application?
Removes the fear of refactoring
Let's face it, we've all worked on legacy applications that we were scared to touch. Imagine if the class you were tasked with modifying were covered by a comprehensive set of unit tests. Picture how easy it would be to make a change and know that all was right with the world because all of the unit tests still passed.
A better architecture
Writing unit tests tends to push your code towards a decoupled design. Tightly coupled code quickly becomes burdensome to test, and so, to make one's life easier, a Test-Driven Developer will begin to decouple the code. Decoupled code is easier to swap in and out, which means that, instead of modifying a tangled knot of production code, often all that a developer needs to do to make the necessary changes is swap out a subcomponent with a new module of code.
Faster development
It may not feel like it at first (in fact, it definitely will not feel like it at first), but writing unit tests is an excellent way to speed up development. Traditionally, a developer receives requirements from the business, sits down, and begins shooting lightning from her fingertips, allowing the code to pour out until an executable application has been written. Before TDD, a developer would write code for a few minutes and then launch the application so that she could see if the code worked or not. When a mistake was found, the developer would fix it and launch the application once again to check whether the fix worked. Often, a developer would find that her fix had broken something else and would then have to chase down what she had broken and write another fix. The process described is likely one that you and every other developer in the world are familiar with. Imagine how much time you have lost fixing bugs that you found while doing developer testing. This does not even include the bugs found by QA or in production by the customer.
Now, let's picture another scenario. After learning TDD, when we receive requirements from the business, we quickly convert those requirements directly into tests. As each test passes we know that, as per the requirements, our code does exactly what has been asked of it. We might discover some edge cases along the way and create tests to ensure the code has the correct behavior for each one. It would be rare to discover that a test is failing after having made it pass. But, when we do cause a test to fail, we can quickly fix it by using the undo command in our editor. This allows us to hardly even run the application until we are ready to submit our changes to QA and the business. Still, we try to verify that the application behaves as required before submitting, but now we don't do this manually, every few minutes. Instead, let your unit tests verify your code each time you save a file.