Since I started working, a common thing I have heard from agile coaches, senior developers, or consultants is the importance of practicing test-driven development (TDD). At my previous company, TDD was a far-off land. A utopia that if we just focused and adhered to its principles, we’d receive ease of development, safe refactoring, and engineering bliss to which every other XP practice paled in comparison.
I never got there.
At DICK’s Sporting Goods, the teams on which I’ve worked have practiced TDD religiously. It is no longer some fairyland, make-believe place. It’s our natural state of working. It’s as common as breathing. It’s actually hard for me to write code today without using TDD. So, do I think it’s worth it? Yes!
What is TDD?
- Do not write any production code until you have a failing test
- Write the minimum amount of production code to get the test to pass
- Keep all tests passing
Another description of this process is called “Red, Green, Refactor”. Create a failing test (red), make it pass (green), clean up the code along the way (refactor).
Test driven development seems easy on the surface. It’s just a few steps, but it really requires a shift in the way we think about writing software. Instead of jumping straight to a solution in the code, we are forced to think about the simplest things the code should do and build up from there.
We consider edge cases and what we expect the code should do in cases of exceptions. It requires us to know those “gotchas” up-front, since our test cases are only as good as the known requirements.
Positives of Practicing TDD
A superficial stat often used to measure code quality is the amount of code covered by tests. By utilizing test driven development, code coverage becomes a given. By making sure every bit of production code first has a test surrounding the functionality, code coverage will naturally increase and be at or near 100 percent.
Another advantage of using TDD is confidence in adding new or altering existing logic. Bugs most often creep up when modifying existing code. Introducing unintended side effects during a code change will be caught when sufficient tests are written during TDD. Also, TDD allows a shift-left testing strategy. This means catching and fixing bugs earlier in the lifecycle of the code is cheaper and easier than later (i.e. in production).
Most of us work in a face-paced environment. What might be true or needed today may change tomorrow. As such, it’s important we write code that is easily maintainable. Part of maintainability is the ability to refactor potentially large parts of applications. I can speak from experience that TDD has allowed multiple teams on which I’ve worked to refactor nearly entire applications safely and securely when requirements changed. Without sufficient unit tests, these refactors would have been headaches at best and disasters at worst.
Negatives of Practicing TDD
“We need to go fast!”
One negative of TDD is you’re trading time now for speed later. While always writing the test code first may slow down the initial development speed, the overall pace of the product can move much faster in terms of future maintenance and modification rate.
“This is weird.”
It feels unnatural…at first. Thinking about and writing test code first goes against everything you know. It feels funny thinking about the simplest thing the code should do, instead of just implementing the whole thing in one go. Like most things, learning takes time, and it’s a different curve for everyone. Stick with it, practice it often, and you’ll begin to see the real benefits of writing tests first.
“Not everything needs tested.”
If something is important enough to have production code associated with it, it’s important enough to have a test. Yes, this even includes REST controllers and repositories. You guys are testing your controllers and repositories…right?
“We have QA. They’ll find any bugs we missed during development.”
More and more companies and teams are going to automated testing. Quality assurance, at least to some degree, falls on the development team. It’s more important now than ever to find, fix, and prevent your own bugs, which is what TDD aims to do. Besides, even if you have test engineers as a “backup”, it’s quicker, easier, and cheaper to find bugs earlier or prevent them in the first place.
Thinking of tests before seeing “real” code on the screen can be off-putting. So many of us were trained to get it working first, then worry about tests second. TDD really does help to ensure the quality of your code.
Our test code becomes just as important as our production code. Our tests should live, grow, and be maintained just like our production code. This means our tests should also be DRY, readable, and maintainable.
As engineers, we’re the ones who have to support the software we write. We’re the ones who get called in the middle of the night for a critical production issue. Wouldn’t it be better if that bug could have been avoided all together? The answer is yes!
Becoming disciplined in TDD will truly make your life easier as a software engineer. It will lead to safer code changes, greater confidence during deployments, and fewer interruptions to your sleep schedule.