A lot has been written about the benefits of unit testing. As someone who was originally skeptical about investing a lot of time into writing tests instead of production code, I just want to bring up a few things that made me change my mind.
A good design is also an easily testable design
Being a business major slash self-taught coder, meant that I originally approached software development mostly from a coding perspective, i.e. writing some code to solve a problem. The basic process for me was: write code, compile and run to make sure it works. Obviously, this doesn’t work so well for large, complex programs. But once I was at the point where I had a large program, I had already written so much code with all these dependencies that it was almost impossible to separate out the different modules to do proper unit testing. So except for a few shared components with no external dependencies I gave up on the idea of unit testing and never used it much. As a result, I feel really uncomfortable when I have to go back to this project now and make changes or fix bugs, because I can never be sure that I don’t break something.
So for my latest project (which is in C#) I decided I was going to look into object-oriented design-patterns to help me in coming up with a design that was modular and would make it easy to test individual components early on. The early on part was important, because this project had a bunch of rather abstract parts that I needed to develop first, but that on their own wouldn’t really be a program I could run and test manually. Also, a lot of the other components that these parts would later work with, didn’t exist yet. Because waiting with testing until the other components were written wasn’t an option, I looked to dependency injection for the answer. This did not only help me with keeping dependencies between my classes in check, it also allowed for easily testing individual components in isolation from the rest by using mock objects. Here is a pretty need intro that has convinced me to write more testable code with dependency injection.
Proper tooling support
Maybe the biggest hindrance for me to doing regular unit testing in Delphi was that writing and running tests required me to switch to a different project, recompile and launch the test application before I could even run the first unit test. I think in order to get the most out of unit testing, it has to be as little an interruption to the normal coding process as possible, allowing me to make changes to the code, run the unit test and two seconds later see that I didn’t break anything.
This means, the testing infrastructure has to be integrated right into the IDE and allow running a test with the click of a single button. Thankfully, the unit testing functionality in Visual Studio courtesy of the ReSharper plugin (the absolutely essential plugin that every .NET developer must have, period) is almost all of that. It is integrated right into the IDE and takes just one click to run one test or even all unit tests at once. Unfortunately, the start-up times of these tests can be quite long for some reason, even when the test itself is just a few lines of code. Don’t know why that is.
Maybe the biggest benefit in addition to being able to test isolated components early on is the good feeling I get after having refactored something or made some other changes and all tests still run through without errors. This is true for when I work on my own code, but it’s even more true for those projects that I have inherited from someone else. These are the kinds of projects where the guy (or girl, though it’s usually a guy) that originally wrote the code is no longer there, and no one really knows how the code works or let alone has the time to dig into it to get a good understanding. Most of the time, the code is also a mess, because let’s face it, we feel that all code that’s not written by ourselves and/or older than a year or so, is garbage. It’s just a fact of life that programmers hate old and love new code, because it’s easier to write code than to read code.
So here’s what I did this one time when I had to rewrite a portion of someone else’s program that originally was extremely monolithic, meaning most of the functionality was contained in only a couple of huge classes that were pretty much impossible to test but extremely easy to break by making modifications in the wrong place: First, I wrote a unit test for what the module I was working one should be doing. This is one of the few occasions where I wrote a test before I wrote any other code. I then extract the original code from the rest of the program and ran the unit tests on it. Most passed, though some didn’t (hence why I had to rewrite the code in the first place). After correcting/rewriting the code I ran the tests again to make sure my change worked as expected. It did. Eventually.
I guess what I should have done if I wanted to be really thorough, is write unit tests for the rest of the program, too, or at least for the modules immediately adjacent to the one I changed. But as is often the case, time was of the essence, because let’s face it: writing a lot of tests does require more time, initially. And this can be hard to justify to impatient customers when the actual fix is only a couple of lines of code. Even when writing tests once it is really an investment that pays off later at every new revision, when you can have confidence that these changes don’t break any of the code written earlier.
When a colleague was trying to sell me on unit testing he said “the green bar is addictive”, referring to the progress bar in most unit testing frameworks (see this screenshot of csUnit, for instance) that turns green when all tests were passed. At the time I thought he was exaggerating, but it’s true. Having all these tests in a project complete without error can be a huge motivator. Especially in like the early stages of developing a headless server application where there isn’t anything really visible to show for your development efforts. Be warned, though, that end users are not impressed when you show them a bunch of successful unit tests instead of the application they have been eagerly waiting for. The addictive quality of the green bar must be geek thing, I guess.