Why TDD?
So, you have joined a new team or a new company. You have been going through the code for a while and think you are finally getting your head around the code that’s been written. You have been handed a bug to fix. You plough through the code, think you spot the issue and apply the fix. You test it the best way you can and think you have covered everything. But something still irks you. So, your eternal conscience speaks to you and asks you to cover all your bases. You run up to the QA on your team and ask them to pay special attention to your bug fix.
Now you feel you have done everything you can and try to sleep easy that night.
I don’t know about you, but I have done this quite a number of times in my career — Not being able to say with a certainty that a bug fix will work after you fix it!
Since this blog is about TDD, this might be the point I think I should say, that’s why should do TDD. But, I’ll get there the long way.
First let’s talk about why unit testing helps.
Instead of giving you an ‘academic’ reason why you should write unit tests, let me try by doing this in a practical way.
Let’s say in your code you have a list of ShareTransaction objects and you need to create a report that lists all share transfers that have taken place
- from Alex to Bob
- in the current financial year (1st Apr — 31st Mar)
- and are more than 1000 shares at a time.

Fairly simple? Just a little filter on a Java stream. Have you spotted the problem yet?
I’ll give you a hint, check the date checks.
It does not include a transaction that happened on the 1st of April and also excludes a transaction on the 31st of March. Both technically happened on the current financial year and needs to be counted. The correct code should have been
(txn.getDateOfTransaction().isEqual(financialYearStartDate) ||
txn.getDateOfTransaction().isAfter(financialYearStartDate)) &&
(txn.getDateOfTransaction().isEqual(financialYearEndDate) ||
txn.getDateOfTransaction().isBefore(financialYearEndDate))
But you didn’t spot this. You tested this locally, without testing the boundary cases. Your QA might spot it. Do you know how much of data setup needs to be done by your QA in order to test boundary cases? It consumes a lot of their time, and if they find a bug in a boundary condition, then they will raise it, you will fix it and then they have to (probably) re-create their test set up in order to re-test the fix. Imagine the time wasted!
Software development, especially, Agile software development is all about reducing the feedback cycle. You iterate often by releasing small chunks of functionality to your users. Why? to reduce the feedback cycle.
Reducing the feedback cycle starts at development. Why do you write unit tests?
The first and foremost reason you write unit tests is to reduce the feedback cycle.
Creating test data that checks boundary conditions is trivial at a unit test level.

The second reason why tests help. Checking Edge cases is simple with unit tests.
Let’s say your product owner has now given you a requirement that too many share transactions that are exactly a 1000 shares in number are slipping through. He wants them included in the report as well. You can easily make this change with one small change in your filter —
// change txn.getNumberOfShares() > 1000 totxn.getNumberOfShares() >= 1000
Again, you test this change locally. You set up data and test transactions with exactly a 1000 shares and it works. You push it in. Little did you know, that there is another piece of code sitting non-conspicuously that relies on the check to be txn.getNumberOfShares() > 1000.

The minute you added a ‘>=’ sign in the filter this code starting breaking. This code was another report that had to report on all share transactions that was less than or equal to a 1000 shares.
This is the third reason why tests help. It helps determine if there are other parts of your code that might break because of your code change.
Often, you face this situation. You keep circling back to a piece of code. This code has been poorly written and its really hard to understand. You dread coming back to this piece of code, cause you know you have to read through this horrible method that stretches 50 lines in total and you need to immerse yourself into it for about an hour to understand what it does. You find the problem and fix it, only to come back and repeat the exercise 2 weeks from now.
You know this code needs to be refactored. But you fear touching it. What if it breaks? How long will it take?
This is the fourth reason why tests help. Refactoring safety. Reduces the fear of changing your code. If you fear touching your code, that project is doomed cause change is the only constant. Everyone knows that.
Tests offer a way to understand your code. A complicated method can be best described by tests that call that method. Take a look at the tests for the filter that you can write:

This is the fifth reason why tests help. Tests serve as living documentation of your code. You can also write all your acceptance criteria as tests like above.
Now for the last part. Why write tests before you code? Does it really matter that it was written before or after you write your code? Yes it does!
- Remember the first reason? Reduce the feedback cycle. Ensuring you have a test before you code, will give you the chance to validate your code immediately after you write it.
- You want to refactor that code now? You are covered. Go for it. You already have a test for it which you know will break in case you do something wrong.
- You write your code first, you might or might not go back and write a test for that code you just wrote. We are all human. When we have already written our logic, the chances are, you are only going to write just enough tests to keep the coverage detector happy. One happy test case and I am out of there! Thats not going to cut it. The negative tests might not work. You have not considered all scenarios in which your code might be called.
- Have you heard of YAGNI? You aren’t gonna need it! How many times have you written a lot of code only to throw it away in the end cause actually that was not what was required in the first place! Writing a test before you wrote so much code, would help stop the madness.
Writing a test before you write code feels very counter intuitive. If you are going to try it for the first time you are not going to get it immediately.
I would like to leave you with a quote by James Shore.
The basic steps of TDD are easy to learn, but the mindset takes a while to sink in. Until it does, TDD will likely seem clumsy, slow and awkward. Give yourself two or three months of full time TDD use to adjust.