Jump To Section
Developers know that the more they test their code, the better their chances are of developing high-quality software, and with Test-Driven Development, or TDD, they’re not only able to validate their code but also automate their testing process in order to keep the code simple, clean, and bug-free.
But even though TDD has been around for several years, there is one big misconception about what TDD is actually about.
Is TDD just about testing code?
We’ll uncover the answer to this question, the true essence of TDD, and how you can use it to improve the quality of what you develop in this article.
What is TDD and What it is Not
Contrary to popular belief, it’s not a method, but more of a mindset toward designing or developing your code.
Before getting into how TDD works and what process is used to implement it, it’s crucial to break some of the misconceptions around what TDD is:
- TDD is NOT just another method of testing code or a QA replacement
- Writing unit testing is a method of practicing TDD
- For each function, you write code to validate your expected flows
- TDD will increase your development cycle
Most people think TDD is about testing code, whereas in reality, TDD goes far beyond simply testing code.
Usually, when we think about testing, the process goes that we write the code first and then test it. TDD offers a whole different approach to software development.
In TDD, the cycle is reversed: you don’t write the code first, but instead, you write your tests.
- TDD is about driving development by test.
“Life is a highway,
I wanna ride it all night long.”
What does Rascal Flatts have to do with TDD?
You write code, and when it fails, you keep writing until it eventually passes; your code is your highway.
So, as the name suggests, TDD is about driving development by test, where you write your test first and then write your code instead of the other way around.
If anything, this further strengthens the concept that TDD is not about testing the code, but rather it’s about an approach to software development where the main focus is ensuring that our code is doing what we expect it to do.
- TDD is a way to verify everything you create. All dev activities are organized around the need to verify what it is that we create. In TDD, therefore, the primary focus is to be able to verify that whatever we create is of good quality and that it can provide the intended results.
- The essence of TDD is to work incrementally on your design
The Value of TDD
Code is weird, unusual, complex, and fragile. One tiny error can invalidate the result.
Even the strongest developers make mistakes – after all, they’re only human, and minor mistakes can happen by anyone.
It’s also interesting to note that the very nature of TDD is to iterate and improve. Step by step, TDD makes it easy to introduce new features incrementally, and in each iteration, we can catch and resolve the defects found during testing. Researchers have found that most problems found in production are caused by simple programming mistakes.
“Approx. 60% of all the issues found in production are due to trivial mistakes.”
A simple mistake can cause entire systems to fail.
A study of 198 production failures found that 58% of the failures were due to trivial mistakes that could’ve been caught before deploying to production. This means that if we checked that our code actually did what we thought it did, we’d improve our reliability by over 50% – substantial cost savings and, most importantly, improved user satisfaction
And while we do have tools that catch some mistakes, and our IDE will give us great feedback that is fast and efficient and maybe highlight some errors, the IDE won’t catch all kinds of issues.
In software development, TDD is a huge step forward because of this very reason: it ensures that we catch the issues that the IDE could not catch earlier.
How TDD works
There’s a lot that TDD has to offer, and understanding its most significant benefits requires a look into the way it works. The following is a summed-up version of the workings of TDD and what happens at each stage:
Writing and checking that the test fails
- We can check the mini specification by executing before writing the behavior and checking that it fails.
- Before even writing the behavior, the focus should be to write the test and to make sure that the test fails.
Of course, the first time, the test will fail because there’s no code.
- If it fails, that’s a good sign – it means we wrote something useful, so now our next focus can be fixing the test.
Improving quality with each iteration
- When we fix the test, what we really want to do is implement our behavior. But then, in the very first iteration, we won’t be trying any fancy things or getting creative with the code.
- At this stage, we just do the bare minimum to pass the test, and once it has passed and we’re good with it, then in the second iteration is where we can bring in our creativity. This is where we can add more quality, improve our code with every iteration, and, most importantly, ensure it fulfills our intent.
Writing code to fulfil the intent
- Next, we write some code to fulfill that intent, and we write just enough code to get to the passing test with that failing specification – the bare minimum to meet that mini specification.
Run test on TDD again to confirm it passes
- Next, we run our test again to confirm it passes. Now we are in a stable state. All our specifications are passing.
Safety check
- Once we run our test, we have the safety check that guarantees that if anyone tries to change the code or wants to modify it, we can run it and still be able to catch any issues with our approach. This safety check guarantees that our test will detect any issues before it goes to production.
The open “code” road is ahead of us to express our intent in the form of test mini specification, and here’s where we divide and conquer: we test mini specifications for one behavior, and then we test another one. Following this, we accumulate and record all the requirements we intend to implement. TDD gives us an independent path verification that the coding actions matched our intent. It offers a way to verify that what we have coded matches and implements our intent.
Evolving Your Code
Now, we have more time to improve our code and bring more quality and refinement to the code. This is true since we have our tests embedded in the CI/CD pipeline and ready to catch any defects that could have been introduced by the change
To make our tests/ code more expressive, we can start changing and modifying it to make it more general, readable, and simpler. And to do so requires a three-step process known as the Red-Green-Refactor cycle.
The Red-Green-Refactor Cycle
What is the Red-Green-Refactor Cycle
In the Red-Green-Refactor cycle, the process of TDD is broken down into three stages:
Red: Write a test that fails
Green: Write code and see it pass
Refactor: Make the code great
Why use Red-Green-Refactor
Through the cycle Red-Green-Refactor cycle, we try to improve the quality of the code to reach a stage where the code is:
- Easy to read
- Easy to change
- Workable
- Testable
- Easy to maintain in the future
- Simple and efficient
And also to achieve the following attributes of good code:
- Modular
- Loosely Coupled
- Cohesive
- Separation Of Concerns
- Information Hiding
Takeaway
With TDD, it has become easier to achieve the attributes of good code, as mentioned above.
When we try to refactor our code to achieve these attributes, TDD gives us the assurance that we have already tested any changes that we want to make, and if any issues arise with our code, they will be detected before production.
The outcome of the TDD approach is a unique test, and these unique tests can be integrated into the Continuous Integration/Continuous Delivery (CI/CD) process. With this integration, what we want to achieve – and what TDD helps us achieve – is that whenever someone changes the code, the process of TDD will run. As part of that, we can find any defects or issues before the code goes into the QA environment and, of course, before it goes into production.
In software development, TDD is one of the top approaches to tackling complex development and ending up with clean, workable code. To learn more about other methods and what is best suited for your business, this article about TDD, BDD, and DDD is not one to miss!