This blog post is the first part of a two-part series on unit testing. The second part is posted here.
My inspiration for writing about this topic comes from leading and helping to facilitate several workshops and events as part of my time here at Greater Sum. One thing I have noticed is that testing does not come easily to some developers, myself included. My hope is to help you understand why testing is important and give you some easy tips on how to write good tests.
First of all, trying to find a concrete definition for a unit test can be a little tricky, so I decided to use Martin Fowler’s explanation. Martin Fowler is the author of the book Refactoring: Improving the Design of Existing Code and he sums it up like this,
“Firstly there is a notion that unit tests are low-level, focusing on a small part of the software system. Secondly unit tests are usually written these days by the programmers themselves using their regular tools – the only difference being the use of some sort of unit testing framework. Thirdly unit tests are expected to be significantly faster than other kinds of tests”.
I think the main thing to realize about unit tests is that they are low-level. With a unit test, you are testing one small unit of the code. These tests are different than integration tests, the next level of testing. Unlike unit tests, integration tests test how units work together. In a unit test, we are not necessarily testing how things work together, but rather how they work separately. Unit tests are typically written by the developers themselves. I’ve mostly seen unit tests written in conjunction with TDD, though this is not always true, this is the most typical use case. And, since these tests are low-level and we run them often, we need them to run quickly.
So why should we write unit tests? Tell me if this story sounds familiar:
A team decides that their test code should not be maintained to the same standards of quality as their production code. So long as the test code works, and so long as it covers the production code, it is good enough. From release to release the cost of maintaining the team’s test suite rises. Eventually, it becomes the single biggest complaint amongst the developers. When managers asked why their estimates are getting so large, the developers blame the tests. In the end, they are forced to discard the test suite entirely.
Without a test suite, they lose the ability to make sure that changes to their code base work as expected. Without a test suite, they can not ensure that changes to one part of the system does not break other parts of the system. So the defect rate begins to rise. As the number of unintended defects rise, they start to fear making changes. They stop cleaning their production code because they fear the changes would do more harm than good. Their production code begins to rot. In the end, they are left with no tests, tangled and bug-riddled production code, frustrated customers, and the feeling that their testing effort has failed them.
Sound familiar? This scenario was actually paraphrased from a story of a team out of Robert C. Martin’s Clean Code, and It’s a sad story. One lesson that we can learn from this story however, is that maintainable and clean tests are just as important as maintainable and clean production code. One of the biggest mistakes I see when developers first start learning TDD is writing bad tests. So how do we avoid this pitfall of bad tests? We learn to write good ones. Robert C. Martin has a great chapter in his book Clean Code specifically dedicated to writing unit tests, but if you’d like to learn more there will be another post in this series called How to Write Unit Tests.