Do you have experience with Test Driven Development (TDD)?
This question is often asked in the interviews for software Engineer.
When I've been asked that question, I used to reply this way:
Yes I do. I think it's very important to write the tests before the code.
Just like that.
Even though my answer was shallow, the interviewers were pleased with it, and they would rarely go deeper with other questions on the topic.
My knowledge of TDD was shallow as my answer.
What I knew about TDD was: writing tests before the code and using a mock or a stub when using a real collaborator was too complicated.
I was not sure about the difference between a mock and a stub.
In other words my knowledge was poor and I was missing many important details that make TDD effective.
Because of lack of TDD experience, I used to take "shortcuts" and write tests after code, with all the drawbacks.
Why TDD can help you to become a better developer?
Some the advantages are:
- Make explicit what you want to achive.
- Keep you focused.
- Help you to think all the various scenarios.
Today I've embraced TDD practices and I noticed on myself a boost of confidence in my coding and on my productivity.
This is what used to happen to me many times, when I was not used to TDD.
When I had to develop a new feature, and I had the solution "in my mind", I would jump on the keyboard writing the code, trying to see the feature coming alive as fast I could.
So the very next morning at the stand up meeting I would say to my workmates
It's done! ... I just need to add a couple of tests
Once again (in case you miss it)
... there are only a couple of tests missing
That was almost never the case.
I used to miss something, for instance:
- A scenario that I haven't initially thought.
- Input that I did not consider.
- A state of the system that doesn't get along with with certain input.
So the issue was either the feature was not fully working or scenarios were missing.
Sometimes a single overlooked scenario was hiding a monster! Unplanned work that would took days to be done.
What you can do in those situation where you've claimed "it's done" but it is NOT?
Rush, rush, rush and overtime!
Does it sound like a recipe for bad code?
What was wrong with my approach?
1. It is very likely to get lost in the details of the implementation.
Let's say I wanted to develop a happy path for a new feature.
I started to create a class/object with a certain responsability, then I added some collaborators to perform different duties, then just another one for that special case, and so on ... until I'd get totally lost.
Yes, I just gets lost.
My brain could not keep up with all the details of the implementation.
All those objects, method calls, interacting each other to solve a problem. It's just too much to cope with for my little the human brain.
Starting writing test, helps to clarify and keep track who are the actors in the game, and what is the goal.
2. It is easy to overlook scenarios and edge cases
Due I need to keep in mind lots of details and focus on the happy path, I often just don't consider certain scenarios.
Sometimes I add comments and TODOs in the codebase, but still without TDD approach it's not easy to keep track of all scenarios.
When you write a test before your code, you are considering a certain scenario. After you make it works, it is easy to copy it and consider different set of input and expectation.
In other words, once you have your first test set up with the Arrange Act Assert (or Given When Then) pattern, you can easily play arranging and asserting different scenarios of the behaviour (Act) under test.
3. Writing test later (or no test at all) is a waste of time
When I do not write tests at first, I still need a way to run my code to prove that it is working as expected.
This is called manual test: running the application in local and manually test the changes that have been introduced.
Immagine you want to test web form. You'd need to fill in every fields in the form, confirm it and verify the expected result.
Have you done it?
Do it again and again at every change in the code!
This way is inefficient and time consuming why I could have even considered it?
Times ago my TDD skills were weak. TDD is something simple in concepts, but still requires quite a bit of practice and knowledge of the test doubles (Dummies, Stubs, Spies, Mocks and Fakes).
Thoughts like these flew in my brain:
Let's make it work, I'll write test later.
This is going to be simple, I do not need any tests.
If I do not write test I can save time.
Those points are all misconceptions.
First, even if you do not write tests at the start, you still need to have tests. One day your code will need to change (new requirements, refactoring, ...) and you'll want to be sure that it works as expected after the changes.
If for some reasons you do not write tests, not even after your code, you are going to spend most of the time debugging and bug fixing, instead of making new functionalities.
To wrap up
Write tests before your code has huge benefits. At first it's not simple to fully embrace TDD, and it's easy to fall in the shortcuts of post-writing tests or even no-tests.
If you practice TDD and stick with it you'll enjoy the benefits.
This post is based on my experience, there is much more to say, I'll write more on the topic.
Thanks for reading.