About unit testing

Unit testing is a software testing method where program units or modules are tested independently of the whole system. There is no exact definition of how much code can be considered a single module. It might be a single object/function or multiple of them that work together.

Unit tests are usually written during the entire lifetime of the module. Theoretically, unit tests are supposed to be written when we first implement the module or even before we start implementing it. They ensure our code is doing what it is supposed to and will keep doing it over time. In practice, we often add and change tests even after the code is already working in production. For example, it’s beneficial to add more tests whenever we fix production bugs and want to make sure they won’t happen again. Another time we want to add or change tests is when the module behavior changes due to changing business requirements.

Many software companies, especially fast-paced startups, give up on unit testing in some parts of their codebases because doing them exhaustively takes too much time. This article offers a middle ground between no tests and all tests- write just a single test.

How much unit testing does one need, anyway?

Writing a full unit test suite is a time-consuming endeavor. It slows the initial delivery of a module and complicates potential behavior changes. Combined with a fast iterative development methodology (Agile or similar), it might lead to lots of wasted development effort that could have been spent elsewhere.

Not writing unit tests at all also ends up being very time-consuming and frustrating. If a developer fixes a bug in a module and doesn’t leave a test, we can’t be sure it will not appear again when we change that module in the future. Repeating bugs lead to lots of wasted effort and, even worse, ruins the user experience and product quality perception.

Some fast-paced companies end up doing “just-in-time unit testing”. This practice usually means not writing any unit tests for a module until they have to, which usually is after the first production bug. The developer who fixes this bug is also tasked with writing a unit test to ensure it won’t happen again. However, the module might not be implemented in an easily testable manner. At this late stage of development, it might be a huge effort to fix that. This situation can turn a simple half-day bug fix into a whole week of refactoring.

It appears that there is a tradeoff between investing a lot up-front and investing a lot further down the road. The total work spent on a module is a convex function with two local maximums (no tests and all tests) and a minimum somewhere in the middle.

A pragmatic solution- one unit test

There are good reasons to choose any point of the testing spectrum. Development velocity is one of many things a company can optimize- different companies and domains have different requirements. However, some companies, like software startups, usually win by being the fastest and most agile in their domain. For these companies, finding the minimum of that convex function is a top priority.

In my experience, the minimum of the “total work spent on a module” function is pretty close to the following method- write just a single unit test up-front. Writing a single unit test is much less time-consuming than a full test suite, so we can usually afford to do it up-front, even for more experimental code. Doing it up-front, when the module is new, and no other module relies on it, means that the overhead of adding that test is also very low. Just create a new test suite file, and that’s it.

Long term, a single unit test might not be as robust as a complete unit test suite. However, that single test will make sure our module is written in a testable manner. It will make it super easy and fast to add more tests in the future when we encounter production bugs or if we ever decide we want more confidence in our code.

This method is the better version of “just-in-time unit testing” practiced in many companies today. We put a bit more work up-front for much easier maintenance. In my personal experience, it is the best compromise for fast-paced companies.

Conclusion

Having some unit tests is much more important than having all unit tests. There is an efficient middle ground between writing no tests and writing all tests- writing just a single one.