2003-10-06

Auf die harte Tour

Ich habe gerade einen üblen Bug in einem meiner Java-Projekte gefunden. Ich benutzte einen StringTokenizer, um einen String in eine Liste einzulesen, der aus per Semikolon getrennten Teilstrings bestand:

StringTokenizer tokenizer = new StringTokenizer(tokenString, ";");
List tokens = new ArrayList();
for(int i = 0; i < tokenizer.countTokens(); i++) {
    tokens.add(tokenizer.nextToken().trim());
}

Wer findet den Fehler? Die Methode countTokens gibt nicht etwa die Gesamtanzahl an Tokens zurück. Sie berechnet, wie viele Tokens noch übrig sind, d.h. wie oft man nextToken() noch aufrufen kann, ohne eine Exception zu bekommen! Natürlich war die Liste dadurch nicht vollständig.

Nun bin ich ja ein guter Junge, also hatte ich natürlich einen JUnit-Test für die entsprechende Klasse geschrieben. Der Test prüft unter anderem, ob eine Liste, die ich aus einem wohldefinierten String erstellt habe, mit der Liste übereinstimmt, die von der zu testenden Klasse aus einem externen String erstellt wird. Und natürlich hatte ich eine ähnliche Schleife benutzt …

Und die Moral von der Geschicht': Schreibt Unit-Tests, aber vertraut ihnen nicht blind. Denn jemand muss auch die Kontrollinstanz kontrollieren.

Übrigens, die korrekte Schleife sieht so aus:

while(tokenizer.hasMoreTokens()) {
    tokens.add(tokenizer.nextToken().trim());
}

Learning the hard way

I just found a naughty bug in one of my Java projects. I was using a StringTokenizer to parse a string separated by semicolons into a list:

StringTokenizer tokenizer = new StringTokenizer(tokenString, ";");
List tokens = new ArrayList();
for(int i = 0; i < tokenizer.countTokens(); i++) {
    tokens.add(tokenizer.nextToken().trim());
}

Can you spot my mistake? The countTokens() method doesn't actually give you the overall count of tokens in the string. It calculates how many tokens are left, i.e. how many more times you can call nextToken() without getting an exception! So of course I was getting too few entries in the list.

Now being the good boy that I am, I had of course written a JUnit test for the related class. Among other things, it tests whether the list parsed from a well-known string was the same as the one parsed from a string from an external source by the class to be tested. Of course, I used a similar tokenizer loop …

And the morale is of course: Write unit tests, but don't trust them blindly. After all, somebody has to control the controller.

By the way, the correct loop is this:

while(tokenizer.hasMoreTokens()) {
    tokens.add(tokenizer.nextToken().trim());
}