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. 🙂
Leave a Reply