10 December 2012

JUnit 4.11 - What's new? Hamcrest 1.3

JUnit 4.11 is a major release of the popular testing framework. JUnit now supports version 1.3 of Hamcrest. Hamcrest is a wonderful library to allow matcher objects for match rules to be defined declaratively. JUnit pre 4.11 uses these matchers, and indeed includes version 1.1 of the hamcrest libraries. Which is a problem because hamcrest is now version 1.3, and some of the method signatures have changed.

Let’s have an example. Using the following test as an example:

import static org.hamcrest.collection.IsCollectionWithSize.hasSize;

public class HamcrestTest {
  @Test
  public void test() {
    List<String> list = Arrays.asList("one", "two");

 // test that the collection has a size of 1
    Assert.assertThat(list, hasSize(1));
  }
}

If we compile this and run with JUnit 4.10 and hamcrest-all-1.3.jar.

$ java -cp "hamcrest-all-1.3.jar:junit-4.10.jar:." org.junit.runner.JUnitCore hamcrest.HamcrestTest

and we get

There was 1 failure:
1) test(hamcrest.HamcrestTest)
java.lang.AssertionError: 
Expected: a collection with size <1>
 got: <[one, two]>

This seems to work, but there are two things to note here. The first is that the message is wrong. It’s actually using a different matcher. It should be:

Expected: a collection with size <1>
 but: collection size was <2>

The real problem becomes obvious when we swap the order of the classpath, and put junit before hamcrest.

$ java -cp "junit-4.10.jar:hamcrest-all-1.3.jar:." org.junit.runner.JUnitCore hamcrest.HamcrestTest

There was 1 failure:
1) test(hamcrest.HamcrestTest)
 java.lang.NoSuchMethodError: org.hamcrest.Matcher.describeMismatch(Ljava/lang/Object;Lorg/hamcrest/Description;)V

The problem is that JUnit 4.10 includes some of the org.hamcrest 1.1 class files, so if you mix and match a class from the junit jar (version 1.1) and the hamcrest jar (1.3), then, unsurprisingly, it doesn’t work. This is not good.

So, to the fix. JUnit 4.11 no longer includes the org.hamcrest classes. All of the methods in org.junit.matcher.JUnitMatchers have been deprecated: just replace them with the equivalent from org.hamcrest.CoreMatchers. Use the hamcrest matchers directly. junit-dep is now also deprecated, because hamcrest is now a direct dependency.

So, if we run the above with junit 4.11, we get the correct error message:

$ java -cp "junit-4.11.jar:hamcrest-all-1.3.jar:." org.junit.runner.JUnitCore hamcrest.HamcrestTest

1) test(hamcrest.HamcrestTest)
java.lang.AssertionError: 
Expected: a collection with size <1>
 but: collection size was <2>

1 comment:

Anonymous said...

Thanks for the post! Being new to hamcrest and starting with version 1.3, I was confused why some examples I found using hamcrest.core.Matchers wouldn't work.

You're write-up help me see the issue immediately.