Saturday, October 24, 2009

Testing Exception Messages, JUnit 4.7 Edition

Historically, JUnit has not elegantly supported verification of expected-exception messages in unit tests. Some time ago, I wrote a post about techniques for handling this situation. Brian Brooks (clearly a diligent web searcher) found that post and was kind enough to send me a message pointing out that the latest version of JUnit, 4.7, finally includes support for this incredibly important and common testing scenario.

In honor of this exciting occasion, I wanted to post an example. Here is the class I'm going to test:
public class PositiveInteger {
 private int num;

 public PositiveInteger(int num) {
  if(num <= 0) {
   throw new IllegalArgumentException("Number must be positive");
  }
  this.num = num;
 }
}
Let's test -- hmmm -- the constructor, I guess. I created two test cases -- one which passes, and one which intentionally fails (just to show the resulting output):
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;


public class TestPositiveNumber {

 @Rule
 public ExpectedException thrown = ExpectedException.none();

 @Test
 public void testConstructorInvalid_PASS() {
  thrown.expect(IllegalArgumentException.class);
  thrown.expectMessage("Number must be positive");
  new PositiveInteger(-5);
 }

 /**
  * Intentionally-failing test to demonstrate the results
  * when checking an exception message.
  */
 @Test
 public void testConstructorInvalid_FAIL() {
  thrown.expect(IllegalArgumentException.class);
  thrown.expectMessage("Not gonna pass");
  new PositiveInteger(-5);
 }
}
JUnit now supports a style embodied in "rules", which get marked with the @Rule annotation and provide a means by which the test execution behavior can be modified. You can see that here with the "thrown" field, of type "ExpectedException". It would be great if a) that field didn't have to be public, and b) the functionality could be implemented using a local variable, but unfortunately the Java language won't currently support such an implementation. So a public field it is. In a test, you can configure the rule. Here I do that with calls to the "expect" and "expectMessage" methods. When JUnit executes the test case, it checks the result against the rule and fails if the rule is not matched. In the test case that does not pass, the failure takes this form:
java.lang.AssertionError: 
Expected: (exception with message a string containing "Not gonna pass" and an instance of java.lang.IllegalArgumentException)
     got: 

 at org.junit.Assert.assertThat(Assert.java:778)
 at org.junit.Assert.assertThat(Assert.java:736)
Very useful. Thanks to Kent Beck for adding this support.

Saturday, September 26, 2009

URLEncoder Demonstrates API Design Hazards

I had to use java.net.URLEncoder yesterday. This experience was a great reminder of how annoying this API is. It demonstrates a couple of issues, so I thought I would comment on it here.

The first thing you can see here is how important it is to get the API right the first time, lest things go downhill from there. In its original form, URLEncoder provided only one static method which encoded a string as "application/x-www-form-urlencoded" (suitable for issuing an HTTP request). This method has the signature encode(String), and it used the platform's default encoding. Unfortunately, this is technically hazardous, because URL encodings are supposed to always be UTF-8. Therefore, wiser heads built and used replacement classes (such as URLCodec from Apache Commons).

Java 1.4 finally "fixed" this deficiency in the platform with a new encode(String,String) overload, the second parameter being the name of a character encoding to use. The old encode(String) method is marked as deprecated. The documentation itself states that: Note: The World Wide Web Consortium Recommendation states that UTF-8 should be used. Not doing so may introduce incompatibilites.

I don't know, but I'm guessing that the powers-that-be would have liked to make encode(String) do UTF-8 encoding. But they felt constrained by the legacy decision to use the platform default, in the name of not breaking any existing code. Never mind the substantial odds that any such existing code is already broken in this case wherever the default encoding is not UTF-8.

But wait, it gets worse. The encode(String,String) method also throws UnsupportedEncodingException whenever (surprise!) the second parameter does not refer to a valid encoding. I'm no rocket scientist, but it seems to me that if you're following the W3C recommendation and always passing UTF-8, you don't have to worry about this exception being thrown. And yet because the exception is checked, you have no choice but to handle it and clutter up your code, perhaps with a catch block that just does assert(false).

This all begs the question, if the second parameter should always be "UTF-8" for some definition of "always", why should the API require a second parameter at all? I submit that a better approach would have been to create, instead of or in addition to encode(String,String), an encodeUTF8(String) method that always did UTF-8 encoding without the added hassle of passing a (constant) parameter and dealing with a checked exception...

Sunday, September 13, 2009

"How to Think About Algorithms" Book

I have been gradually making my way through "How to Think About Algorithms" by Jeff Edmonds.

I have recently recommended this book to several people, and I wanted to mention it here, too. Edmonds uses what I'll call a more intuitive approach to algorithms that I think would be appealing to those with an engineering mindset. Here I'm constrasting it with the texts that I've used as a student in algorithms courses -- the classic Sedgewick and CLRS books.
(Maybe more importantly in this economy, though, the subject matter and exercises in this book are a rich source of review for those preparing for tech interviews!)

Wednesday, September 2, 2009

Tailing Inside Eclipse

I'm currently working on a project that writes to a log file inside my workspace when I test-execute it. Obviously it's useful to actually view that log file once in awhile, but to do so inside Eclipse requires regularly F5-refreshing it in the Project Explorer. I started to wish that Eclipse would prompt me to automatically reload the file when it detected modification, like some text editors do. But then it occurred to me how convenient it would be if I could actually tail the log from inside Eclipse itself. After all,

The less I leave the IDE, The more productive I can be.

(That rhymes.)

This seemed like an obvious enough desire that somebody would have built a plugin for it already, and sure enough, a search turned up a couple of different examples. The one that I went with is called NTail. It works like a champ. It also has a few extra nice features, like the ability to highlight lines in the file based on a regular expression. If this sounds useful to you, I'd encourage you to check it out.

Saturday, August 8, 2009

Changing Return Values to Exceptions, Java/AspectJ Edition

In the last couple of posts, I've been talking about APIs that communicate errors via return values, and how to map those to exceptions. As we saw most recently, one reasonably effective approach in C++ makes use of macros, but it requires a degree of development discipline, and the resulting code remains somewhat unsatisfactory.

In this post, I will present an approach for Java that mitigates these issues. Specifically, you can neatly map return values to exceptions using AspectJ.

(Note that I am using Eclipse for this work. It's great that AspectJ is now an Eclipse project, because it can be incredibly frustrating to use without solid IDE support.)

I'll start with some Java math utility routines that are roughly equivalent to what I was working with in C++. One difference is that in Java, I don't need a custom sqrt() wrapper because the Math.sqrt() method already uses a return value, Double.NaN, to indicate error.
package com.scottmcmaster365.math;

public class MyMathUtils
{
  public static double radiusFromArea(double area)
  {
    return Math.sqrt(area / 3.14);
  }

  public static double circumferenceFromRadius(double radius)
  {
    return 2 * 3.14 * radius;
  }
}
I want to wire up an aspect that runs when these functions are returning, checks for NaN, and throws an exception. With a bit of understanding of AspectJ, this is quite simple:
package com.scottmcmaster365.aspects;

import com.scottmcmaster365.math.MyMathUtils;

/**
 * This aspect maps double NaN return values to runtime exceptions.
 * (For instructional purposes only.)
 * @author Scott McMaster
 *
 */
public aspect MapNaNResultToExceptionAspect
{
  pointcut mathClass() : within(MyMathUtils);
 
  pointcut mathMethods() : mathClass() && execution(* *(..));
 
  after () returning (double d) : mathMethods()
  {
    if( Double.isNaN(d) )
    {
      throw new RuntimeException("Result is NaN");
    }
  }
}
Hopefully even if you're not familiar with AspectJ, that makes at least a little sense to you. Otherwise, I suggest checking out a good tutorial. But in a nutshell, the pointcuts pick out the methods in the MyMathUtils class, and the after advice grabs the return value and allows us to inject code at compile-time.

With this aspect in effect, the client for finding the circumference given the area of a circle will throw a RuntimeException during the call to radiusFromArea():
final double TESTAREA = -1.0;
double radius = MyMathUtils.radiusFromArea(TESTAREA);
double circumference = MyMathUtils.circumferenceFromRadius(radius);
System.out.println(circumference);
That's exactly what I was after: Clean code that doesn't have to check return values explicitly or via macros. Score one for AspectJ. In fact, by making a very minor change to the pointcuts and advice spec, I can even fire the aspect at the Math.sqrt() call site, which (in a more complicated example) narrow the defect down even further. I'll leave this as an exercise for the reader unless someone comments that they want to see it.