I am a strong believer and applier of Test Driven Development. I reached the point that I can not touch the production code unless I have a failing test for that, even if the change is small or simple. There are many advantages of using and applying TDD, which are the followings:
Thinking from the client perspective
Using TDD forces us to think from the client perspective and to ask the questions: What is the use case of the given component (library, api, module, class, function)? What does the user or client of the given component want to achieve? What are the error and edge-cases? Applying TDD shifts our focus from the implementation side to the client side. And basically it is what we ultimately want to achieve, focusing on the user and on fulfilling their needs and wishes. As the Scrum framework also says, one of our most important task in software-engineering is to make the customer happy. And the the customer can either be an every-day user of an application (such as my father using gmail for mailing) or a consumer of our engineering products (such as another engineer consuming our API’s, libraries or classes).
Since the software evolves with the time, it has to be maintained, improved and extended. In order to change the existing code with high confidence, you need a proper test suite acting as a safety net. So if you screw up the existing functionality somewhere in the code, you will have a corresponding test indicating that you broke a functionality. Without this safety net, you would be lost and there is a big chance that the bug will be noticed by the users, firstly. What will your user think? He or she will surely not be happy, and as already noted above, one of our main goals should be making the user happy.
Many can say that this safety net effect can also be achieved with testing afterwards, so writing tests after the implementation is done. Theoretically it can work, but according to my experience, it rarely happens, and the programmers tend not to write tests afterwards, just only a few for the sake of having them and verifying of the happy-flow works as expected. They will definitely lose most of the benefits of TDD. Let’s go for the next one!
Low-level living documentation
Have you ever integrated a third party library into your project? Many libraries have nice big and long documentation about the intent and all the technical details. And at the end you had some appendix of examples for the usage. Which one did you read? Of course you read the examples because you were so eager to leverage the given library in your project. Examples are about how the code works, how it can be used. Tests do exact the same, it reveals the usage for the given component, what the expected inputs, outputs and side-effect are. Therefore your tests will act as a low level living documentation, describing how the given code works. When there is a new developer on the team, I always tell her to check the tests if she wants to get to know more about the use cases and the domain.
Clean code by design
By applying TDD we can achieve clean code by definition. To practice TDD, you need to go through three development phases, namely RED, GREEN and BLUE phase. It is a fairly simple process, but all the more powerful. First of all, in the RED phase you write a failing test for a given use case. After that, comes the GREEN phase, where you write the production code to make the failing test pass, to make it green. And last but not least, in the BLUE phase it is time to refactor your code without changing its behavior, for example improving readability or removing duplication. As you see, by design you always need to go through the BLUE phase and to spend time on refactoring . Therefore you will end up with a cleaner code base.
Decoupling by definition
If you want want to test a software component, you have to do it in isolation. If you isolate something, you decouple it from other parts of the system. Actually all the SOLID principles are based on decoupling, therefore by applying TDD we will end up with a decoupled components, resulting in a more maintainable, extendable and flexible software.
Always working code
One of the beauty of TDD is that you see your code every minute working. It gives the developer confidence about the working state of the software, while enabling fearless and incremental creation and extension of the software. The developer gets continuous positive reinforcements along the way!
Applying TDD is fun and definitely a joyful process. The joy comes from the sense of progress you get from doing it. It is a game-like feeling to structure and write your code in a turn-based way, with exact goals to achieve. Furthermore, the outcomes are visible and almost immediate which can highly boost our creativity.
The three rules of TDD
To practice TDD properly by going through the RED-GREEN-BLUE, you need to follow three important rules:
- 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.
The first rule is quite straightforward and it forces you to strictly start with a test instead of any writing production code.
By the second rule you can’t write any more of an unit test, only a test which is sufficient to fail, nothing more. The idea behind it is that it wants to prevent you writing a tests which would need more complex production solution in the GREEN phase. It would lead you to think on a more complex problem, which will result in slower performance and more errors. Baby steps for the win!
By following the third rule, you will end up with the most minimal production code to fulfill the given test. It helps us to stay away from gold-plating our code and to shift focus on implementing the desired feature. In order to write more production code in the solution space, you would need another failing tests. Therefore this rule implicitly forces you to write separate tests for each behaviours.
The main delusion about TDD
While more and more people are practicing TDD, many still say that using TDD is useless and has many drawbacks. I heard from several people that TDD take so much time and does not worth the effort. Well, it is true that it takes additional time to write more code but is just a short-term lose since on the long-term you will definitely benefit from using TDD. Finding a bug in design time is much cheaper than finding it in run-time, not to mention that what happens if the user finds it, decreasing our reputations with it. Furthermore, planning and executing a new release with the fixed bug takes time and money, so we should prevent it as much as we can. As a matter of fact, TDD is one of the best “tool” for preventing bugs! Next to that, you can imagine how much a new developer in the team would spend on figuring out what is the usage of the components without having proper test suite revealing the functional and low-level intents. Time is money, we should spend it wisely!
As we can see we can gain many benefits by using Test Driven Development. Starting from achieving the desired code behavior, through producing bug-less and clean code, ending with a useful low level living documentation. In order to practice it properly, we need to follow the three main rules within the RED, GREEN and BLUE development phases. Although TDD is not a silver-bullet, and won’t solve all your problems while developing software, but it is definitely one of the best design process you can apply!