How would you define Unit Tests?
That’s not a rhetorical question: I am asking (and perhaps
challenging) you to come up with a crisp definition of a Unit Test. A
definition that is short, is not circular, and does not need to refer to
a specific programming language or unit-testing framework.
If you find that hard, you are in good company. “Like most software development terminology, [unit testing] is very ill defined.”
My colleague, James Saffell,
recently gave a talk on “Consulting, Communication, and You”. In it, he
provided a definition of unit-testing that he had heard from a
technologist at a client:
Unit tests are tests that validate the assumptions made by the developer who is writing the code
The technologist had used this definition to claim that unit tests
weren’t necessary, as “all they did was reflect the assumptions of the
developer writing the tests themselves”.
I think that it is an excellent definition. However in marked
contrast with the technologist who coined it, I think it justifies why you should do as much of your testing doing automated unit tests as possible.
Imagine that you’re writing a web application for a small-yet-growing
online retailer that does business in the United States. (Not the most
original example, but bear with me!) It is likely that you’ll have to
get the customer’s shipping and billing addresses at some point. It is
also likely that the shipping rates to Alaska and Hawaii would be
different from the remaining 48 states, because of geography.
Let’s say that as a small company, you can currently bill customers
living in Alaska or Hawaii; however, you’re unable to ship your
merchandize to these states because the higher costs don’t yet fit into
your business model. All of this creates interesting scenarios. The
delivery team discusses these scenarios and gets to work. Imagine that
the developer responsible for the checkout feature writes these unit
it "should allow all 50 states while entering billing address" do
it "should allow contiguous 48 states while entering shipping address" do
These unit tests represent requirements directly, so you could say
that no explicit assumptions have been made by the developer thus far.
Let’s assume that the developer implements the shipping address list
by removing “AK” and “HI” from the 50 states. It achieves the desired
result. The developer, being diligent and well-versed in the art of
testing, writes this additional unit test:
it "removes AK and HI from all states to get list of shippable states" do
While she’s at it, the developer also writes a test validating that all state abbreviations are two letters long:
it "should abbreviate state names to 2-letter abbreviations" do
You build and deploy your software and life is fine. Until someone in
marketing remembers Puerto Rico, Guam, and US Virgin Islands.
You decide that you want to expand your business to allow customers
with billing addresses in these areas. You still cannot ship to these US
territories, however, you decide that it is a valid compromise for your
Your team gets together to deliver this new feature: billing
addressess should now show “PR”, “GU”, and “US VI” in addition to the 50
states. You use “US VI” to ensure that your users clearly understand
your inability to accept “British Virgin Islands” as part of a billing
Aren’t you glad now that your developer wrote all those unit tests validating her assumptions!
As part of implementing this new feature, the team will quickly
discover – a failing test will remind them – that it’s no longer correct
to only remove AK and HI from the list of acceptabile billing states to
get shipping states. They will also discover that not all state name
abbreviations are two letters long. Those are the obvious and direct
benefits of the unsolicited unit tests the diligent developer wrote.
There are other, subtle yet equally important benefits. In fixing all
these unit tests, the developers feel unease at writing something like
it "should abbreviate state names to 2-5 letter abbreviations" do
They realize their unease comes from the fact that Puerto Rico, Guam
and US Virgin Island are not states. They see that all throughout the
code, these entities are named “state”. When they look further and talk
to the whole team, the architect, the business analyst, and the two test
engineers confirm that they refer to these places as “states” in their
artifacts, too. This starts a conversation about the validity of the
Domain Model. The team realizes that the collective assumption they made
about naming the geographical units within the United States is
incorrect. After a bit of research,
they agree on the term “Territory” to represent all fifty states and
territories. They recognize that this is a better term when the company
expands operations to Canada (Provinces and Territories) and Mexico (Estados).
As a team, they decide to make changes in the software, documentation,
and other artifacts to reflect this. Most importantly, they change their
daily vocabulary to use “Territories” to refer to these places.
Unit tests are indeed testable assertions about the assumptions that developers make. That’s why you should insist on having them as executable code.
Image credit: @essenj