How to use Hamcrest in Java to test for a exception?

Michael Osofsky picture Michael Osofsky · Dec 31, 2014 · Viewed 28.2k times · Source

How do I use Hamcrest to test for an exception? According to a comment in https://code.google.com/p/hamcrest/wiki/Tutorial, "Exception handling is provided by Junit 4 using the expected attribute."

So I tried this and found that it worked:

public class MyObjectifyUtilTest {

    @Test
    public void shouldFindFieldByName() throws MyObjectifyNoSuchFieldException {
        String fieldName = "status";
        String field = MyObjectifyUtil.getField(DownloadTask.class, fieldName);
        assertThat(field, equalTo(fieldName));
    }

    @Test(expected=MyObjectifyNoSuchFieldException.class)
    public void shouldThrowExceptionBecauseFieldDoesNotExist() throws MyObjectifyNoSuchFieldException {
        String fieldName = "someMissingField";
        String field = MyObjectifyUtil.getField(DownloadTask.class, fieldName);
        assertThat(field, equalTo(fieldName));      
    }

}

Does Hamcrest provide any additional functionality above and beyond the @Test(expected=...) annotation from JUnit?

While someone asked about this in Groovy (How to use Hamcrest to test for exception?), my question is for unit tests written in Java.

Answer

mystarrocks picture mystarrocks · Dec 31, 2014

Should you really need to use the Hamcrest library?

If not, here's how you do it with Junit's support for exception testing. The ExpectedException class has a lot of methods that you can use to do what you want beyond checking the type of the thrown Exception.

You can use the Hamcrest matchers in combination with this to assert something specific, but it's better to let Junit expect the thrown exceptions.

public class MyObjectifyUtilTest {

    // create a rule for an exception grabber that you can use across 
    // the methods in this test class
    @Rule
    public ExpectedException exceptionGrabber = ExpectedException.none();

    @Test
    public void shouldThrowExceptionBecauseFieldDoesNotExist() throws MyObjectifyNoSuchFieldException {
        String fieldName = "someMissingField";

        // a method capable of throwing MyObjectifyNoSuchFieldException too
        doSomething();

        // assuming the MyObjectifyUtil.getField would throw the exception, 
        // I'm expecting an exception to be thrown just before that method call
        exceptionGrabber.expect(MyObjectifyNoSuchFieldException.class);
        MyObjectifyUtil.getField(DownloadTask.class, fieldName);

        ...
    }

}

This approach better than the

  • @Test (expected=...) approach because @Test (expected=...) only tests if the method execution halts by throwing the given exception, not if the call you wanted to throw the exception threw one. For example, the test will succeed even if doSomething method threw the MyObjectifyNoSuchFieldException exception which may not be desirable

  • You get to test more than just the type of the exception being thrown. For example, you could check for a particular exception instance or exception message and so on

  • The try/catch block approach, because of readability and conciseness.