What did Unit Tests ever do for us Anyway?
Six months ago my fellow dev and I were handed a new project to work on. We had just started to work together, our domain knowledge was zero, and 50% of us (me, that is) had zero experience of C#. The significant risk that this formula involved was obvious, especially on the faces of our project manager and the division head.
Something special was required. Luckily this guy would not shut up about this. Dependency inject he said, unit test he said. Having both experienced projects where the thought of changing code struck fear into our very hearts, we were up for giving it a go.
It started slow, that much is true. Writing one to two lines of test harness code for every line of “real” code is hard to accept. It’s counterintuitive given the business pressures you are under. Test code can be so simple that it feels worthless. Of course thing.Increment() returns thing+1, what else would it do?!
I felt that too, and often, but sticking with it (thanks to the support of the team as a whole) brought with it a significant reward. It is between doubt and boredom that magic happens.
Unit tests allowed a clear and concise communication of intent. For us this meant that we could compensate for some of our lacking in domain knowledge by increasing our ability to converse about the state of the code. I could easily point to a test to explain a problem I was having, instead of being stuck for words.
Code rot starts instantly. Having tests from the beginning allowed us to make sure that once the code was correct, it stayed correct. For a project where there were no gurus to save us this was incredibly valuable. Without unit tests to help us, plugging one hole would spring a leak elsewhere and we would almost certainly have ended up racing to the bottom as deadlines approached.
Knowing when we hit the Target
One pivotal component had to store and sort data on disk. We didn’t know how we would do that at first so we came up with the interface for the component and an in memory implementation. The tests for this interface acted as a target for us to hit when it came to implementing the on disk component. Once our on disk implementation passed the same tests we knew that our work was done and it satisfied the same criteria!
Testability gave us Flexibility
Making something testable through dependency injection means that it is also flexible. Having gone through this process we were able to port our application to multiple new DB platforms, each one taking roughly two days to complete. Each solution depends on the very same engine, we just had to write the custom pieces to be injected. Testability means being able to mock out parts of the system easily, those mockable parts make your system ultra-flexible.
We are on to another project now. One full of legacy code and no unit tests. Our eyes have been opened to what quality code can be and it is already influencing how we work on this new challenge. Try it and stick with it. Aiming for 100% test coverage but settling for 60% is enough to raise the quality of your code and your job satisfaction immeasurably.