Are Your Unit Tests Readable?

Gal Koren
skai engineering blog
3 min readJul 22, 2017

“If you have questions about my code, go find answers in my unit tests.”

yeah, right…

Maybe happens once a year.
I mostly prefer reading the production code because, to be honest, the quality of a typical unit test is quiet poor.
For comparison:

  • Extracting out classes/methods
    Production — yes
    Unit tests — rarely
  • Names of methods/fields
    Production — chosen carefully
    Unit tests — no need to think. We have conventions like “testMyMethod_success”
  • Side effects
    Production — considered evil
    Unit tests — who cares

In other words, in unit tests, we only care that our code works.

Inspired by Cucumber

In Kenshoo, we use the Cucumber framework for writing our end-to-end tests.
Using Cucumer, the scenarios are written in a language named “Gherkin”, which is not a programming language.
Gherkin does not support variables, nor loops, conditions or any flow-control.
It supports only one thing: invoking other methods with arguments.
As a result, the high-level test method is extremely readable.

When I have keywords in AdWords:
| text | bid |
| shoes | 500000 |
And I download keywords from AdWordsThen I expect keywords in DB:
| text | bid |
| shoes | 0.5 |

Inspired by that style, if we try hard enough, we could come up with a similar code in Java:

{
When_I_Have_Keywords_In_Adwords(
AdWordsKeyword().withText("shoes").withBid(500000)
);
List<Keyword> keywordsInDb = downloadFromAdWords(); assertThat(keywordsInDb, containsKeywords(
DbKeyword().withText("shoes").withBid(0.5)
));
}

To construct a complex Keyword object inline with the when clause, the example uses the builder pattern.

For the assertion, the example uses the Hamcrest assertThat method with a custom collection matcher (method containsKeywords), which makes the Java statement looks like an English readable statement.

The above example shows that there is a way to extract methods and even classes (matchers and builders) to keep the scenario itself high-level and human-readable.

Naming the Scenarios

A Java statement may be composed of method calls, just like an English statement is composed of verbs, adjectives and nouns.
For instance:

assertThat(namesOf(myDogs), contains("Milo", "Rex"));

Here, method “namesOf” serves as an adjective for “myDogs”, while method “assertThat” serves as a verb.
As words which are parts of a sentence, we choose our method names carefully to be short.

The test method, however, is not a part of a sentence. It is the sentence itself, which describe what is going to happen in this scenario. Therefore, it should be long.
Note that nobody is going to call your test method within a statement. Instead, the testing framework will find your test method using reflection.

Once you switch your mindset to that revolutionary idea, you can write the names of the tests to document the user-requirements for your class.
For instance:


@Test public void the_database_should_be_accessed_only_once_for_each_bulk();
@Test public void
the_bid_value_in_Kenshoo_should_be_the_AdWords_micro_amount_divided_by_1000000();
@Test public void
an_unsupported_status_value_should_be_converted_to_status_UNKNOWN();

Reading these signatures, you actually have a full spec of your class which is always up-to-date.
Could actually you think of any better documentation?

--

--