It's Time to Stop Lying About TDD
James Burt - Senior Java Developer
2022 Jul 1 - 1min. Read
Copy Page Url
It's Time to Stop Lying About TDD.
Many of the CVs that I see as a Java interviewer for Mindera mention test-driven development (TDD). It’s in most of the job roles I see advertised too. But I am fairly certain that most people are not actually doing TDD.
How do I know this? Because when we do live-coding exercises, very few candidates write tests, let alone do them before they add any code. It’s the same with the code samples sent in — very few of them include tests. Maybe these developers are using TDD, but they are not demonstrating it.
Here’s a hint: tests in a Mindera coding exercise (or even a well-reasoned argument for not using them) is one of the main things we’re looking for. Head over to our careers page to learn more about our culture and hiring journey.
What’s the distinction between TDD and unit test coverage?
It’s important to appreciate the difference between TDD and just having unit test coverage. TDD is a formal method — it uses a cycle of write test → write code → refactor to drive development. No code should be produced without a corresponding test. This is distinct from just having a unit test coverage threshold, which is what most people seem to mean when they put TDD on their CVs.
Writing the tests before the code forces us to think about how to test the code we are writing. It demands that we consider how our classes work, and how they fit together. It makes refactoring part of any programming task.
Yes, it is possible to write the tests after we’ve written the code. A lot of developers work that way. The problem here is that the code is not written for testability.
In many cases, developers have ‘god classes’ which seem to do everything. All the business logic is crammed into single classes which do all the work required in a business action. Testing these then requires a large amount of set-up. Tests are cut-and-pasted to make minor changes to cover a deeply hidden statement in a private method. Over time, these classes become harder to maintain and a group’s development speed slows down.
The code produced by TDD is fundamentally different to that produced by code-then-test techniques. Code produced with TDD tends to use composition over inheritance, with multiple classes collaborating.
These classes usually obey the single-responsibility principle, focussing on doing one task well. If your code has lots of classes with names ending in Service or Manager it’s a good indication that they are important but nobody is quite sure what they do.
TDD produces code which is easy to both test and refactor, because that has been a focus at every point in its development. On the contrary, code-then-test techniques produce difficult legacy code, with sprawling and complicated unit tests. They also tend to have less coverage than TDD systems.
If you are working on a project with a code coverage threshold then look at how the actual coverage compares with that threshold. If the difference is minor then it’s a sure sign that people are adding just enough code to pass the threshold. This suggests a lack of commitment to proper testing.
If you use TDD then you need to really believe in it
At this point, you’re probably assuming that I use TDD every day. And I have to admit that I’m a hypocrite. I’m lying too. Even though I believe in TDD, it’s hard to force it into projects at a late stage.
When there are god classes with large numbers of tests coupled to this structure, it’s difficult to justify the time to pick them apart, and for colleagues to review the changes. I believe in TDD, but it’s hard to do without support.
The main problem with TDD is that it takes time to trust it. It does feel slower initially, until you get comfortable with the new way of working. That’s why it’s good to practise. You can run coding dojos with colleagues, looking at practice exercises or taking simple bits of production code, and work with them. TDD is a skill and needs honing.
And, if you do decide to pick apart those massive god classes, there are techniques for that. Michael Feather’s book Working Effectively With Legacy Code offers strategies for improving test coverage on existing code.
Conclusion: we need to be honest with ourselves about TDD
Most of us developers are lying about TDD. And we need to admit that to ourselves before we can do something about it.
If you believe unit test coverage is important, do you believe we should use a coding technique that produces high coverage? If you have TDD on your CV do you do your best to use it?
Lying about TDD, to ourselves and others, is a trap. If we’re honest, we can move to understanding unit tests better, and produce more maintainable and cost-effective code.
Copy Page Url