assert != assertEquals (duh)

It’s probably not great for my reputation to show how I made a very silly error, but since I did it so consistently I thought showing it might help somebody avoid it.

My Groovy course materials consist of far more scripts than classes. That’s probably not surprising, given that teaching Groovy involves writing lots and lots of quick-and-dirty examples. The problem with scripts, though, is that they’re not as easy to test as classes. With classes, I can create a class that extends GroovyTestCase, put in my tests and go. With scripts, though, the same process would require either executing the script from a Groovy class and checking the binding properties, or simply using assert statements.

In general, I chose the latter. In my scripts, I tried to complete each with at least one assert call that I could use later to validate the script.

Sounds like a reasonable approach, right? Sure, until you start going too quickly. What’s wrong with the following code?


// inject demo
def strings = ["Hello","World"]
int totalLength = strings.inject(0) { len, s ->
    len += s.size()
}
assert 10, totalLength

The code is intended to be a trivial illustration of the inject method for lists. The result is supposed to be the sum total of the lengths of all the strings in the list. Ignoring that there are many other ways to solve that problem, the difficulty here isn’t the inefficiency of the algorithm. No, it’s a trap that as a long-time Java developer I found very easy to fall into.

The problem is with my assert statement. The intention is to assert that the total length of the strings in the list is 10. Sure enough, executing this code has no errors. That’s not a big surprise (for me), because at first I started with a println statement to see what the value should be, and then I replaced println with an assertion.

Unfortunately, though, my assertion didn’t prove anything about the script. That becomes obvious if I add another line to the program:

assert 50000, totalLength

which passes just as easily.

What’s wrong? I’m using assert as though it was a two-parameter method, like assertEquals in GroovyTestCase.

What I’m trying to do is to specify the right answer followed by the actual test. What I’m actually doing is asserting that the first argument (a literal number) is true, and supplying an error message to print if not. By the Groovy truth, any non-zero number is always true, so my totalLength never needs to be converted to a string and printed as the the error message.

What I really want to use, of course, is

assert 10 == total, "total should be 10"

I think this is an easy trap for Java developers to fall into, because they’re not accustomed to the Groovy truth. In Java, only a real boolean expression can be true or false, not just a number.

What’s truly embarrassing is how many of my scripts were just asserting that a non-zero value was true, which always works.

Once I realized my mistake (because somebody pointed out one of my bad examples), I had to go back and fix all of my tests. They’re better now. 🙂

About Ken Kousen
I teach software development training courses. I specialize in all areas of Java and XML, from EJB3 to web services to open source projects like Spring, Hibernate, Groovy, and Grails. Find me on Google+ I am the author of "Making Java Groovy", a Java / Groovy integration book published by Manning in the Fall of 2013, and "Gradle Recipes for Android", published by O'Reilly in 2015.

4 Responses to assert != assertEquals (duh)

  1. Dierk König says:

    The real omission was something else: forgetting to do ‘fail-first’. Every assert you are writing should first fail before you make it pass by providing the actual implementation. That’s what’s sometimes called ‘test-the-test’.
    The rules are like this:
    – always start with a red bar (failing test)
    – find the shortest way to make it green (test passes)
    – keep it green while refactoring
    It’s great that you show your students the value of testing and assertions. Also showing the above ‘mantra’ would be the next step.

    cheers
    Dierk

  2. Ken Kousen says:

    Yes, that makes perfect sense now. I need to fail first to avoid just this sort of error. I know I’ve heard that before, but you know what they say: “Good judgment comes from experience, but experience comes from bad judgment.”

    This weekend I attended the No Fluff, Just Stuff conference in Boston and watched Neal Ford do a great example of Test Driven Design. He broke a problem down into tiny pieces and gradually implemented it by first writing tests for each part and then building it back up. Afterwards I tried that on a Grails service I was writing and it was great. It was so much easier than how I normally work.

    I think that’s what I never realized — writing the tests and building up the result gradually isn’t just a good idea; it’s actually an easier way to work.

    Thanks for your comment. (And thanks again for GinA!)

  3. boardtc says:

    Can you explain what the inject method does? Should
    assert 10 == total, “total should be 10″
    read a
    ssert 10 == totalLength, “total should be 10″

  4. Ken Kousen says:

    The inject method on a collection is used as an accumulator, where the first argument is the initial value.

    The statement

    int totalLength = strings.inject(0) { len, s ->
    len += s.size()
    }

    means that “len” is a variable whose value starts at 0 (the argument in parentheses) and is then returned when the loop completes. The code is equivalent to saying

    int totalLength = 0
    strings.each { s -> totalLength += s.size() }

    I think using inject here is considered groovier.

    My problem was that my next statement:

    assert 10, totalLength

    should have been

    assertEquals 10, totalLength

    because when the assert keyword is given two arguments, the first argument is just a string reported when there’s an error, and the second argument is evaluated to see if it’s true. In my case, since totalLength was not zero, I always returned true, no matter what constant I supplied as the first argument.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: