If you are interested in reading this article in Spanish, check out my blog The Developer's Dungeon
Until 2 years ago I had never written a single unit test in a professional environment, yes you heard that right, I am ashamed to admit it but in the first 3 years of my career no one had ever asked me to write a test and I couldn't care less.
I didn't knew how they worked and much less understood the benefits they could bring, sure I knew the theory and knew from blogs that they could be useful but genuinely I did not consider them important.
Then I read "Clean Code" and "The Clean Coder"[Robert C. Martin] and guilt took over my body. I realized that during my career I didn't present a single repeatable proof that my software was working, it was indeed functioning out of pure luck and only tested by me in very specific situations.
I also realized how irresponsible I was for delivering products like this and that I was not only prejudicating the users of my software but the craft I so deeply love.
Suddenly I remembered many times bugs would come up over and over again from parts of the code that were already "finished", how a change in an unrelated piece of software could cause tons of issues that were already manually tested and "fixed".
I also understood that what I felt before doing a refactoring was fear, deep fear because of the errors I could bring if I would open that door.
It was at that moment when I decided to take a stand and make a change for myself, just like Michael Feathers says "Legacy code is code without tests" I committed that any code that I would produce would not be legacy, therefore will always have tests.
I understood that development is not finished until your code has a suite of tests to prove it right, or at least not wrong.
Since then, after making my code work I would proceed to write tons and tons of unit tests, trying to cover as much as possible when a bug appeared I would fix it and then write a unit test for it, and while my code and self-esteem as a developer got better my fear did not go away.
In fact, every time I had to refactor something, that fear would creep up on me again, having a big suite of unit tests would not prevent it, what is worst is that when a bug was found and there was a unit test that should already catch that, but it was passing for a different reason, my confidence on that test suite would dramatically drop. Many times I went home questioning if all that work doing unit tests was even worth it or I was just fooling myself.
Then I read "Test Driven Development By Example" [Kent Beck] and a new possibility came knocking on my door. Maybe the order of the development process did matter and doing things "Backwards" could have some benefits?, I decided to give it a try.
A little context:
TDD is a practice in which you let your tests guide the design of your solution, following this process will make your code Decoupled and Testable.
Usually, when we want to develop a new feature we immediately get into the code, start typing like crazy and come up with a very crappy but functioning solution, then we write a unit test and call it a day. With TDD this process would be backwards.
- Ask yourself, what should your software do? in which situations it might not work? then you proceed to write your first test to prove your assumptions, this test will obviously fail since there is no implementation yet.
- Then you need to do the smallest and easiest thing possible to make your test pass, just focus on making that light go to Green.
- Finally, when the test is passing, you refactor your implementation and fix all the "bad things" you allowed to happen in the previous step, but always making sure that light is still green.
This cycle is called Red Green Refactor.
Now that we understand the basics let's go over the reasons why I fell in love with TDD:
By following this path you always start in a stage where your code is not working but you make it work, this presents the first reason I love TDD, CONFIDENCE
When you first see your code and test fail but then after some changes you make it pass, you get an instant gratification that fuels up your confidence and trust about your implementation, it is a sutil change until you are facing that mighty refactoring, then you notice that you are not afraid anymore, if you break something, your tests are there guarding your back.
Usually, when you have to develop a new feature, you have to touch existing code, you change a few things here and there, but then you end up in a place no developer wants to be, the code is not working anymore and you did so many changes you are not sure which one broke it, you spend a lot of time trying to find that change or sometimes you roll back all your changes and start again from zero.
While facing this situation I encountered another reason for loving TDD, LOW RISK.
During TDD you are in a very short and fast-paced cycle of Red Green Refactor, every run introduces new changes in a very incremental fashion since the code was working a few seconds ago, it is trivial to find the changes that caused the light to be Red and fix them so we can see our precious Greenlight again.
Many times when I was going through that phase of writing the tests after the code was done, I encountered an error that would make my whole test suite crumble, I noticed it was because the foundations of that suite were not stable, to begin with.
When you write tests after you wrote the implementation it is very common that unconsciously your tests only happen to take the "Happy path", those places in your code were everything is green, nice and new, you avoid test cases that are hard to test, simply because your code was not built to be tested in the first place.
This situation showed me the way to the third reason, NO FEAR OF EXTREMES.
TDD forces you to think about the tests first, with this mindset you can fully concentrate on the edge cases, the things would normally break your software are gonna be magically covered, you will find a new type of fun in finding those edge cases and make sure they first go Red but in the end, they go Green.
Lastly, and this one is related to the last one. When I first started doing tests my job as a software developer become one of the most boring and tedious tasks in the world, I cursed the moment I made myself responsible for not writing them, this boredom provoked that I would avoid the paths that were very hard to test. But here It came TDD to rescue me, by making the tests first and see them fail and then fixing them I developed an addiction, but don't worry is a good one, I felt addicted to finding errors in my code and fixing them, knowing with certainty that those bugs would not show up again in my code, or at least not in the same way, was the reason that closed the deal, basically TDD MADE UNIT TESTING FUN.
Since then I can't imagine my life as a developer without writing unit tests, sometimes I even write tests in order to reproduce bugs instead of trying them manually.
This addiction I mentioned even moved me to create a VSCODE extension for running Angular unit tests easily in order to follow the fast cycle of Test Driven Development.
I really hope that after reading this article you will find interest in giving TDD an opportunity. Not gonna lie, it is a hard path, I still have to remind myself all the time not to go off the plan and keep doing implementation when I should be writing the next test, but with practice, this road becomes much easier and the benefits much more clear.
What do you say, are you ready to take this road with me?