How Unit Tests Have Helped Me Write Better Code
Rather than say why I think developers should write unit tests, I will instead describe how I've benefited from them.
PROLOGUE
A number of years back, I worked with a lead developer who had a wide breadth of technical knowledge and whom I considered highly intelligent, perhaps even borderline brilliant. He was (and is) a smart individual, but he saw no use in unit tests. He told me that when he first started working for the company, one of the first things he did was to delete all the unit tests, and he even tried to convince me that the practice of writing unit tests was now being considered a thing of the past, and told me he'd forward me a link to an article he'd read stating as much.
I try to keep an open mind, but in this case I wasn't going to be swayed into thinking that something which had proven its worth to me countless times over the years was now without merit.
This interaction is the inspiration for this post and how it's presented: not to say why someone should write unit tests, but rather to tell how, in my own experience, they've proved beneficial.
#1 - They've Helped Me Ensure My Code Actually Works
This is an easy one. Code is only useful if it actually does what it's supposed to, and sometimes we write simple code that should "just work", but we overlook something, and it doesn't. By writing unit tests, I've uncovered issues in my code before going to production and have made sure my code does what it's supposed to.
#2 - They've Helped Me Identify -- And Compensate For -- Edge Cases
This is similar to the point mentioned above. Sometimes we write code with the intended flow, or "Happy Path", in mind, and don't immediately realize some edge cases which could occur. It's happened to me numerous times, and the practice of writing unit tests has helped me catch these potential "gotchas" and handle them accordingly. Recognizing an edge case which could occur and having the code handle it appropriately eliminates unwanted side-effects or problems.
#3 - They've Helped Me Identify Design Flaws
The process of writing a unit test causes you to view your code in a somewhat different way from that of writing the code itself. You view it from the consumer side (i.e. from the point of view of someone using the code). This has helped me realize, for example, when a parameter was needed -- or unneeded -- or perhaps where something was in the wrong place, or dependencies needed to be added, removed, or refactored. It's helped me put my code through the paces before actually executing it. In short, by writing unit tests I've been able identify shortcomings and refine the code I'm testing.
#4 - They've Guarded Against Breaking Changes
EPILOGUE #1 - Integration Tests Are Not Enough
Almost a year after the conversation mentioned at the start of this post, the lead developer in question decided that integration tests would be implemented. He'd set up some tests using Nightwatch and was checking values on a webpage to ensure everything was correct. I sat in the meeting room with him and his developers and listened to them praise this new approach, which they considered simpler than unit testing. They all agreed that unit tests were "a pain in the ass".
But here's the irony: months prior, developers on this team had implemented code to allow website users to renew subscriptions. Everything on the page appeared correct, but under the hood they were overcharging the subscribers. The approach the lead was suggesting with Nightwatch would not have caught this problem -- those tests would've passed, and yet they'd still be overcharging customers. However, proper unit testing, if done correctly, would've caught the problem before it made it into production.
EPILOGUE #2 - Integration Tests Do Have Their Merit
I tend to write integration tests sparingly, focusing mainly on unit tests. But sometimes integration tests can have a lot of value. I wrote some code once to integrate with a third party system, and I wrote unit tests of this functionality, mocking out the 3rd party components. But even with all of my unit tests, and end-to-end testing performed by some of the business users, there were still issues that made it through undetected. I blame myself for not being more thorough in this situation. Sometimes, there's no substitute for the real thing, and when dealing with an external system you may not be able to accurately mock or predict how it will behave.
SUMMARY
In my experience, doing my due diligence and writing proper, correct unit tests has prevented a lot of problems. Whether you do Test-Driven Development or write unit tests after the fact, the benefits will be there. As for the lead developer who inspired me to write this blog post, in the time I spent working him there were times where I'd have to correct problems with his code, code which should have "just worked", but for whatever reason didn't. After finding and correcting the cause of the problem, I'd immediately write unit tests to verify it. Sometimes, my tests would reveal that my fix wasn't complete, and that more was needed to make sure things worked properly. More than one person has joked with me that the lead developer in question really didn't need to write unit tests because I did it for him! Unfortunately, those tests were after the fact and too late.
I also think that some developers neglect writing unit tests because they just don't feel like it. I know the developer I've mentioned didn't. And I think it's only fair that I follow this post up with a later one addressing the criticisms of unit testing.
In my current role as a principal developer, I require my developers to write unit tests before the work is considered code-complete. And in doing so, we've ensured a level of code integrity that we wouldn't have otherwise. It doesn't mean we still don't make mistakes, but the amount of mistakes is greatly reduced thanks to unit testing.
In my current role as a principal developer, I require my developers to write unit tests before the work is considered code-complete. And in doing so, we've ensured a level of code integrity that we wouldn't have otherwise. It doesn't mean we still don't make mistakes, but the amount of mistakes is greatly reduced thanks to unit testing.
Comments
Post a Comment