Why do we write unit tests?

Brandon Salm
Neighborhoods.com Engineering
3 min readSep 7, 2018

--

A few years back, a former colleague — a recent college graduate at the time — asked me “Why do we spend so much time writing unit tests?” I’m not sure present-day me would be too impressed with the answer I gave the junior developer. Considering I was only his senior by maybe 2 years, and didn’t have a ton of experience on the matter… I’m not sure he was sold on my justification of “because you’re supposed test your code.”

It takes quite a bit of time working in the software development field to develop a solid testing philosophy, as it is not something that is typically covered extensively in any college curriculum. Even then, testing philosophies can differ drastically, even among developers working with identical technologies.

It has been several years since I was last asked “Why do we write unit tests?”, but I’ve learned a lot since then. Here of some of my current thoughts on why we should be writing them:

Ensure new units work as expected
When we write a new unit of code, writing tests for that unit is proof that the unit is working as expected, when isolated within the system. In the same vein, writing tests for new units identifies unexpected behavior that may be missed by other forms of testing (E2E, integration, functional, smoke, etc…).

Ensure old units continue to work as expected
When we need to go back and modify an existing unit, as we so often do, the presence of comprehensive unit tests give us confidence that the unit is still adhering to its contracts. When implemented in a meaningful manner, unit tests are essentially a low-level regression test suite. While other types of tests also ensure that functionality is preserved, a unit test failure isolates the unit responsible for the breakage.

Ensure that units adhere to good coding practices
Writing unit tests alongside your code helps keep it well fleshed out, and loosely coupled (as it would be, keeping units loosely coupled is key if we wish to analyze them independently from other units). Units that adhere to good coding practices are easier to change down the road, if and when the the need should arise.

Building a test suite for a unit should be a fairly trivial task. Struggling to write meaningful tests is often a sign that code contains one or more anti-patterns, code smells, or bad practices. Failing to properly mock the internals of a unit can be caused by issues like nested callbacks, coupling to concrete data sources, or violation of principles such as the Single Responsibility Principle and the Dependency Inversion Principle.

Ensure that unit behavior is predictable
Writing unit tests helps force code to be deterministic (or even better, pure!). Writing non-deterministic functionality that has hidden inputs, or uses external state, makes unit behavior less predictable — thus more prone to bugs. This is an extension of the previous point, as a unit that contains non-deterministic factors, side-effects, etc… will be revealed as difficult to test. This is likely a sign that the unit needs reworking, using things like Inversion of Control or Higher-Order Functions, to decouple the unit from its hidden inputs or an external state.

Ensure that units are documented
Writing unit tests is a great way to create “ever-green” documentation for a unit. Unit tests do a great job describing a unit’s behavior, and document the way the unit was intended to be used by the original author. Having this “sample code” in place for future implementors to reference is a wonderful added benefit for writing tests.

While I’m sure there are plenty of other reasons developers have to write tests, these five are the few that I find myself using to justify the extra time it takes to write quality unit tests in my day-to-day.

--

--