9 Unit Tests

The Three Laws of TDD

By now everyone knows that TDD asks us to write unit tests first, before we write production code. But that rule is just the tip of the iceberg. Consider the following three laws:

  1. You may not write production code until you have written a failing unit test.
  2. You may not write more of a unit test than is sufficient to fail, and not compiling is failing.
  3. You may not write more production code than is sufficient to pass the currently
    failing test.
    The tests and the production code are written together, with the tests just a few seconds ahead of the production code.
    The sheer bulk of those tests, which can rival the size of the production code itself, can present a daunting management problem.

Keeping Tests Clean

Having dirty tests is equivalent to, if not worse than, having no tests. The problem is that tests must change as the production code evolves. The dirtier the tests, the harder they are to change.
Test code is just as important as production code. It requires thought, design, and care. It must be kept as clean as production code.

Tests Enable the -ilities

If you don’t keep your tests clean, you will lose them. And without them, you lose the very thing that keeps your production code flexible. If your tests are dirty, then your ability to change your code is hampered, and you begin to lose the ability to improve the structure of that code.

Clean Tests

Readability makes a clean test. Readability is perhaps even more important in unit
tests than it is in production code. What makes tests readable? The same thing that
makes all code readable: clarity, simplicity, and density of expression.
Each of the tests is clearly split into three parts, the BUILD-OPERATE-CHECK
pattern. The first part builds up the test data, the second part operates on that
test data, and the third part checks that the operation yielded the expected
results.

Domain-Specific Testing Language

Rather than using the APIs that programmers use to manipulate the system, we build up a set of functions and utilities that make use of those APIs and that make the tests more convenient to write and easier to read. This testing API is not designed up front; rather it evolves from the continued refactoring of test code that has gotten too tainted by obfuscating detail.

A Dual Standard

The code within the testing API does have a different set of engineering standards than production code. It must still be simple, succinct, and expressive, but it need not be as (memory or CPU) efficient as production code.

One Assert per Test

There is a school of thought that says that every test function in a JUnit test should have one and only one assert statement. This rule may seem draconian, but those tests come to a single conclusion that is quick and easy to understand. Changing the names of the functions to use the common given-when-then convention makes the tests easier to read.
Splitting the tests results in a lot of duplicate code. We can eliminate the duplication by using the TEMPLATE METHOD pattern and putting the given/when parts in the base class, and the then parts in different derivatives. Or we could create a completely separate test class. But this seems like too much mechanism for a minor issue.
I am not afraid to put more than one assert in a test. I think the best thing we can say is that the number of asserts in a test ought to be minimized.

Single Concept per Test

Perhaps a better rule is that we want to test a single concept in each test function. We don’t want long test functions that go testing one miscellaneous thing after another.
So probably the best rule is that you should minimize the number of asserts per concept and test just one concept per test function.

F.I.R.S.T

Clean tests follow five other rules that form the above acronym:

  • Fast – Tests should run quickly.
  • Independent – Tests should not depend on each other.
  • Repeatable – Tests should be repeatable in any environment.
  • Self-Validating – The tests should have a boolean output. Either they pass or fail.
  • Timely – The tests need to be written in a timely fashion. Unit tests should be written just before the production code that makes them pass.
Previous: 8 BoundariesUp: ContentsNext: 10 Classes