picture home | pixelblog | qt_tools

omino code blog

We need code. Lots of code.
David Van Brink // Wed 2007.08.8 23:04 // {java software architecture}

Basics: The Name of the Assertion

The Java JUnit framework is an incredibly useful tool for maintaining code integrity. This note focuses on the tiny but important matter of what messages to insert into your assertion statements.

Basics: The Name of the Assertion

Why Assert?

The philosophy behind unit testing is simple: If you make the appropriate small assertions about your software, you can believe that the system will operate correctly in the aggregate.

The Java JUnit framework provides a convenient way to structure a collection of these assertions. In JUnit, when an assertion is correct, or “true”, nothing happens. When an assertion is incorrect, an error is displayed, and the particular test is ended.

JUnit provides a handful of Assert methods. Each one has an optional first parameter: the failure message. This note shall offer guidance on maximizing the value of those failure messages.

JUnit In One Paragraph

I’ll assume you either know or can easily find out the simplest mechanics of writing and using JUnit. For the purpose of this note, we need merely recall that:

  • assertTrue(boolean b) fails if b is false
  • assertNotNull(Object o) fails if o is null
  • assertEquals(Object a,Object b) fails of a does not equal b
  • …and that each takes an optional first argument with a String failure message.

Evolution of a Test

Let us now examine specimens of a test from various levels of the evolutionary ladder.

Homo Habilis

A test can’t be much simpler than this… or less informative.

  public void testTwoFish() 
    assertTrue(getListOfTwoFish().size() == 2);
junit.framework.AssertionFailedError at dvb.JunitExamples.testTwoFish(JunitExamples.java:12)

Well, it failed. It says right there, “AssertionFailedError”!

Homo Erectus

This version of the test shows the beginning of structure.

  public void testTwoFish() 
    // "expected" goes before "actual"
junit.framework.AssertionFailedError: expected:<2> but was:<3> at dvb.JunitExamples.testTwoFish(JunitExamples.java:15)

Better already! Our distant ancestor Homo erectus knew that assertEquals was a more powerful tool than assertTrue. It tells us that something was 3 but should have been 2. But what? Well, the test name gives a hint. But we can do better…

Homo Neandertalensis

We begin here to see a nearly modern JUnit test. Language begins to play a broader role in survival.

  public void testTwoFish() 
    assertEquals("getListOfTwoFish()", 2, getListOfTwoFish().size());
junit.framework.AssertionFailedError: getListOfTwoFish() expected:<2> but was:<3> at dvb.JunitExamples.testTwoFish(JunitExamples.java:20)

Now the message is somewhat helpful… and naturally raises the question, What did it return, if not two fish?

Homo Sapiens

We now see a specimen of a fully functional JUnit test.

  public void testTwoFish() 
    List<String> x = getListOfTwoFish();
    assertEquals("getListOfTwoFish() " + x, 2, x.size());
junit.framework.AssertionFailedError: getListOfTwoFish() [nemo, mr limpet, flipper] expected:<2> but was:<3> at dvb.JunitExamples.testTwoFish(JunitExamples.java:26)

Do you see it? We’ve made the actual list part of the error message. Handily, Java collections and arrays have a built-in toString() method which lists each element separated by commas. Now, when it fails, the problem is obvious.

Homo Superior

That last specimen may be satisfactory for human programmers, but my standards, and I hope yours, are higher still.

  public void testTwoFish() 
    List<String> x = getListOfTwoFish();
    assertNotNull("getListOfTwoFish() was null",x);
    int size = x.size();
    assertEquals("getListOfTwoFish() " + x, 2, size);

Two final flourishes round out our exercise. First, we check for null. It could happen to anyone, it could happen to you. And secondly, we pull out the size assignment to a local variable. This is a courtesy to those who come after us, stepping through our code.

The Common Theme

There is a common theme in all the above examples: augment the information that JUnit already returns. The assertEquals is wonderful, as it shows what you want and what you got. Our message augments it with a name, a list… a hint. The science fiction writer Theodore Sturgeon invented a symbol for “Ask the next question.”

The idea applies here. What went wrong? Oh, we expected one integer but got a different one. What were they? Ah, we got “3” but it should have been “2”. So what was the third fish?

But Why All That Effort?

In practice we usually write a test once, and, assuming it passes, never touch it again. The above “evolution” may happen as a result of debugging a failure. But: the effort is small to do it up front, when the test is first written. Like most learned behaviors, it may take a few negative experiences before it becomes truly second nature. We all burn ourselves once, and then learn to use potholders. And wasn’t there something about seatbelts?

All images taken from Wikipedia, except Magneto.

oh, i dont know. what do you think?

(c) 2003-2011 omino.com / contact poly@omino.com