44

In JUnit, I'm currently using annotation to expect an exception in my tests.

Is there a way to analyse this exception? For example, I expect a CriticalServerException, but I also want to verify the content of the getMessage method.

Elnur Abdurrakhimov
  • 44,533
  • 10
  • 148
  • 133
Farid
  • 1,542
  • 3
  • 18
  • 27

11 Answers11

68

If you have JUnit 4.7 or above try ExpectedException

There is an example in this question, which is copied below:

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

@Test
public void testRodneCisloRok(){
    exception.expect(IllegalArgumentException.class);
    exception.expectMessage("error1");
    new RodneCislo("891415",dopocitej("891415"));
}
Community
  • 1
  • 1
Filippo Vitale
  • 7,597
  • 3
  • 58
  • 64
21

I'm not sure if you should. Using a try-catch block to check the error message is so junit3ish. We have this cool feature now that you can write @Test(expected=CriticalServerException.class) and you want to go "back" and use try-catch again to fetch an exception you expect, just for checking the error message?

IMO you should stay for the @Test(expected=CriticalServerException.class) annotation and ignore the error message. Checking the error message, which can be changed a lot as it is a more "human readable" string and not a technical value, can also be tricky. You are forcing the exception to have a specific error message, but you might not know who generated the exception and what error message he chose.

In general you want to test if the method throws the exception or not, and not what the actual error message looks like. If the error message is really so important you should maybe consider using a subclass of the exception it throws and check it in @Test(expected=...).

lucrussell
  • 5,032
  • 2
  • 33
  • 39
Progman
  • 16,827
  • 6
  • 33
  • 48
  • +1 for a useful answer; I don't know if I would personally prefer this or not, but it's a good mechanism to know about – Brian Dec 20 '10 at 14:32
12
try{ 
    //your code expecting to throw an exception
    fail("Failed to assert :No exception thrown");
} catch(CriticalServerException ex){
    assertNotNull("Failed to assert", ex.getMessage()) 
    assertEquals("Failed to assert", "Expected Message", ex.getMessage());
}
Toby
  • 9,523
  • 8
  • 36
  • 59
jmj
  • 237,923
  • 42
  • 401
  • 438
2
try
{
    // your code

    fail("Didn't throw expected exception");
}
catch(CriticalServerException e)
{
    assertEquals("Expected message", e.getMessage());
}
Brian
  • 6,391
  • 3
  • 33
  • 49
2
try {
    // test code invacation
    fail("Exception not throw!!!");
} catch(CriticalServerException ex) {
    assertTrue("Invalid exception data", ex.toString().contains("error text"));
}
Alex
  • 225
  • 2
  • 7
2

Use MethodRule as a common solution, if you have many test cases to test

public class ExceptionRule implements MethodRule {
    @Override
    public Statement apply(final Statement base, final FrameworkMethod method, Object target) {
        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                try {
                    base.evaluate();
                    Assert.fail();
                } catch (CriticalServerException e) {
                    //Analyze the exception here
                }
            }
        };    
    }
}

Then use the Rule to your test class:

@Rule public ExceptionRule rule = new ExceptionRule(); 
卢声远 Shengyuan Lu
  • 31,208
  • 22
  • 85
  • 130
1

I don't think there is a way of doing it using annotation. You may have to fall back to try-catch way where in the catch block you can verify the message

Ankit Bansal
  • 4,962
  • 1
  • 23
  • 40
0

Use catch-exception:

catchException(obj).doSomethingCritical();
assertTrue(caughtException() instanceof CriticalServerException);
assertEquals("Expected Message", caughtException().getMessage());
rwitzel
  • 1,694
  • 17
  • 21
0

Look at fluent-exception-rule, it "combines Junit ExpectedException rule and AssertJ's assertions convenience. "

import pl.wkr.fluentrule.api.FluentExpectedException;
...
@Rule
public FluentExpectedException thrown = FluentExpectedException.none();

@Test
public void testDoSomethingCritical() {
    thrown.expect(CriticalServerException.class).hasMessage("Expected Message").hasNoCause();
    obj.doSomethingCritical();
}
Marcin
  • 1,469
  • 14
  • 23
0

If you want to compare message along with exception type then you can try below code snippet.

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

    expectedException.expect(IllegalArgumentException.class);
    expectedException.expectMessage("Parameter is not valid"); //check string contains
    expectedException.expectMessage(CoreMatchers.equalTo("Parameter is not valid")); //check string equals

Note: This will work for junit 4.9 onward.

Meet
  • 242
  • 1
  • 4
  • 13
0

Java 8 solution

Here is a utility function that I wrote:

public final <T extends Throwable> T expectException( Class<T> exceptionClass, Runnable runnable )
{
    try
    {
        runnable.run();
    }
    catch( Throwable throwable )
    {
        if( throwable instanceof AssertionError && throwable.getCause() != null )
            throwable = throwable.getCause(); //allows "assert x != null : new IllegalArgumentException();"
        assert exceptionClass.isInstance( throwable ) : throwable; //exception of the wrong kind was thrown.
        assert throwable.getClass() == exceptionClass : throwable; //exception thrown was a subclass, but not the exact class, expected.
        @SuppressWarnings( "unchecked" )
        T result = (T)throwable;
        return result;
    }
    assert false; //expected exception was not thrown.
    return null; //to keep the compiler happy.
}

(taken from my blog)

Use it as follows:

@Test
public void testThrows()
{
    RuntimeException e = expectException( RuntimeException.class, () -> 
        {
            throw new RuntimeException( "fail!" );
        } );
    assert e.getMessage().equals( "fail!" );
}

Also, if you would like to read some reasons why you should not want to check the message of your exception, see this: https://softwareengineering.stackexchange.com/a/278958/41811

Community
  • 1
  • 1
Mike Nakis
  • 56,297
  • 11
  • 110
  • 142