Arguments in favour of automated software testing are too often presented in a patronising fashion. Developers that don't write their own tests are labelled as "unprofessional" and the code they write as "worthless". These arguments conflate two very different groups of developers: those who don't care enough to test, and those that care deeply but are unconvinced of the benefits of testing. The former group aren't worth convincing, and the latter can't be expected to be swayed by an argument that insults them in this fashion.
So it seems more constructive to focus instead on real, practical benefits of testing, rather than shouting from a perceived moral high-ground. Based on an earlier series of blog posts, this article attempts to do just that. Below I examine key ways in which you, as a developer, directly benefit from writing tests for your own code.
Testing Is ... Instant Gratification
One of the most frustrating parts of the development cycle is the start of a new major release. This is an exciting time, where big new things are planned and frontiers are explored. However, given the size of the problems that are tackled in this phase, roadblocks are always found along the way. The finish line can feel an awfully long way in the distance. Thus it is easy to get the feeling that you are going nowhere: as you have no concrete features to show for your efforts. This is where where testing can help. By isolating and exercising a smaller piece of the puzzle, you can demonstrate progress, if only to yourself. I find that even this small sense of achievement can be strongly motivating. Once the most basic things are working, you can’t help but add that next incremental step. Before you know it, you’re in the zone and things are really beginning to gel. Just don't forget to sleep!
So, next time you get coder's block: write some tests!
Testing Is ... Freedom to Change
Perhaps the single greatest advantage of writing tests is the freedom you feel to change the code under test. All software grows cruft over time, and left unchecked the code will become an unmaintainable mess. Most developers would agree that maintaining code is not the greatest part of the job, and maintaining crufty code is about as bad as it gets. The answer is to refactor and remove the cruft over time. However, we also know from bitter experience that changing working code, no matter how crufty, will introduce new bugs.
This is where the tests come in. If you have maintained a comprehensive test suite over time, you need not fear nasty new surprises. You can refactor as required, and keep your code base much healthier. Now, test suites are not perfect and new bugs are still bound to happen. However, this is mitigated by two factors:
- The difficulty in maintenance of the crufty code itself will lead to bugs.
- A well-written test suite will cover those dark corners where bugs have been found to lurk. In particular, regression tests written to reproduce bugs as they are found are priceless at refactor time.
Testing Is ... A Huge Time Saver
I don’t know about you, but when my new code works the first time I try it I’m not happy, I’m suspicious. This suspicion comes from years of getting things wrong. Typically, each feature will take several testing iterations to get right. This is trivial with an automated test, and time consuming with a manual one. So, although the upfront cost of implementing the test may seem to slow you down, remember that the cost of repeatedly running the test is very low.
Over the long term, the time saved by an automated test suite increases. The most dramatic time savings come when your suite finds a new bug as it is introduced by a developer. With the right process in place, this bug can be fixed before it breaks the stride of the development team, and long before it reaches a customer and leads to a costly support incident. Maintaining the suite is certainly not free, but in my experience this benefit dwarfs the cost.
Testing Is ... Good For Your Design
Fear not, I’m not going to start pontificating about design theory or TDD. Rather, my argument is that making code "testable" also tends to make it clean. I look at this as the tests being a client of my code that help to keep the design honest.
This is most clear when testing at the unit level. The purpose of testing at this level is to isolate a distinct piece (or "unit") of code so that it can tested independently. The better we can isolate the units, the better we can cover the code as a whole without a combinatorial explosion in the possible inputs and outputs. The great thing is that well-isolated units exhibit a key quality: Separation of Concerns. To be truly isolated, a unit should be concerned with only one task. A complex system is much more powerful if it is built out of small, distinct units that can be combined in interesting ways (the "Unix way").
A common counter-example that suggests testability and good design are actually competing goals is the apparent need to access internal unit state in tests. The problem with this of course is allowing the test to access that state is at odds with another key design goal: Encapsulation. In practice, I actually find that the need to access internal state is exceedingly rare. I would go so far as to say I consider it a design smell: if you feel the need to assert over some internal state in your tests, there is a good chance that the unit granularity is wrong (perhaps you have not truly separated concerns). There are exceptions of course, but in most cases refactoring is a better solution to subverting encapsulation. Thus, rather than countering, this example enforces the idea that writing tests drives improvements to design.
Testing Is ... Better Than Debugging
The Google Testing Blog is headlined by a graphic that states:
Debugging Sucks. Testing Rocks.I agree wholeheartedly. Debugging is a fact of development life. It even has its own rewards, such as the sense of achievement when you find and squish a particularly nasty bug. Simply put, however, debugging is a massive waste of time. Any time spent debugging could otherwise have been spent actually writing code, or otherwise being productive.
Testing reduces time spent debugging in various ways:
- Well-crafted assertions in your tests will narrow bugs down right to the source. Operating at the unit test level in particular is more precise than manual testing.
- Automated tests catch problems early in the development cycle, when they are easier to debug. It is well known that the earlier a problem is found, the easier it is to fix.
- Regression tests catch problems that have been debugged before, so you don’t need to debug them again. This is particularly powerful in complex areas where bugs pop up frequently.
Testing Is ... A Worthy Challenge
One of the reasons that I love programming is the challenge of problem solving. On the flip side, nothing is more boring than repetitive, mind-numbing tasks. The great thing is that when something in my job becomes repetitive, I can usually automate it. By programming. Meta.
Why is it, then, that testing has the reputation of being a mind-numbing and repetitive activity? It is almost as if developers believe testing is an unskilled task that is doomed to be menial. In actual fact, automating your tests takes many of the same skills as developing features. If you find it repetitive, ask yourself why and challenge yourself to do something about it! It is a perfect opportunity to put your problem solving skills to work.
For me, automated testing is not so much about quality or "professionalism" as it is about efficiency. There are dedicated developers producing quality code without automated tests, and they are right to be sceptical of arguments that disparage their work. I hope, however, that the benefits I've outlined in this article will encourage such developers to give testing a closer look. Indeed, I hope more developers actually give automated testing a serious trial and begin to reap the benefits for themselves.