16

I have code where I check the exceptions in jUnit. I want to know which of the following is a good jUnit practice?

First

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

@Test
public void checkNullObject() throws CustomException {
    exception.expect(CustomException.class);
    MyClass myClass= null;
    MyCustomClass.get(null);
}

Second

@Test(expected=CustomException.class)
public void checkNullObject() throws CustomException {
    MyClass myClass= null;
    MyCustomClass.get(null);    
}

EDIT: Please note that CustomException is a unchecked custom exception. (Though it won't have any impact on this question).

Raedwald
  • 46,613
  • 43
  • 151
  • 237
Priyank Doshi
  • 12,895
  • 18
  • 59
  • 82
  • 1
    Either a duplicate of http://stackoverflow.com/questions/785618/in-java-how-can-i-validate-a-thrown-exception-with-junit or primarily opinion based – Raedwald Dec 18 '15 at 19:58

2 Answers2

20

It depends what you want to check in the exception. If all you're doing is checking that the exception is thrown, then using @Test(expected=...) is probably the easiest way:

@Test(expected=CustomException.class)
public void checkNullObject() throws CustomException {
  MyClass myClass= null;
  MyCustomClass.get(null);
}

However, the @Rule ExpectedException has a lot more options, including checking the message, from the javadoc:

// These tests all pass.
public static class HasExpectedException {
    @Rule
    public ExpectedException thrown= ExpectedException.none();

    @Test
    public void throwsNothing() {
        // no exception expected, none thrown: passes.
    }

    @Test
    public void throwsNullPointerException() {
        thrown.expect(NullPointerException.class);
        throw new NullPointerException();
    }

    @Test
    public void throwsNullPointerExceptionWithMessage() {
        thrown.expect(NullPointerException.class);
        thrown.expectMessage("happened?");
        thrown.expectMessage(startsWith("What"));
        throw new NullPointerException("What happened?");
    }

    @Test
    public void throwsIllegalArgumentExceptionWithMessageAndCause() {
        NullPointerException expectedCause = new NullPointerException();
        thrown.expect(IllegalArgumentException.class);
        thrown.expectMessage("What");
        thrown.expectCause(is(expectedCause));
        throw new IllegalArgumentException("What happened?", cause);
    }
}

So you can check for the message, the original cause of the exception. For checking the message, you can use matchers, so you can check startsWith() and similar.

One reason to use the old style (Junit 3) throw/catch is if you have specific requirements. There aren't many of these, but it can happen:

@Test
public void testMe() {
    try {
        Integer.parseInt("foobar");
        fail("expected Exception here");
    } catch (Exception e) {
        // OK
    }
}
Steve
  • 1,159
  • 10
  • 14
Matthew Farwell
  • 60,889
  • 18
  • 128
  • 171
  • FYI the `javadoc` link is 404 – Ghilteras Nov 09 '18 at 21:09
  • Using `@Test`to test for exception has two disadvantages over `@Rule` ExpectedException. 1. you cannot assert cause / message. For example `@Nonnull` did not work as expected and we got an NPE from elsewhere. 2. When we mock exceptions, `@Test(expected)` litters the logs with exception but `@Rule` swallows them if the exception matches. – iec2011007 May 04 '20 at 06:57
  • 1
    only the old style ensures the exception is thrown at the intended place! – André Nov 30 '20 at 16:05
0

The second version is definitely the standard way to do it. The old school way to do it, before Junit 4 looked like:

try {
    codeThatShouldThrowException();
    fail("should throw exception");
} catch (ExpectedException expected) {
    //Expected
}

Sometimes you might want to revert to this way, for example if you want to assert something about the message in the exception.

Viktor Nordling
  • 8,694
  • 4
  • 26
  • 23
  • I think even if we want to check something about exception message , we can go with [this way](http://stackoverflow.com/questions/11413922/check-errorcode-with-rule-in-junit) . No need to use this try -fail - catch construct – Priyank Doshi Jul 11 '12 at 06:36
  • I specifically said "before Junit 4". Doesn't `this way` refer to a solution which relies on @Rule, which was introduced in Juni 4? https://www.testwithspring.com/lesson/introduction-to-junit-4-rules/ – Viktor Nordling May 16 '19 at 00:55