关于java:JUnit测试 – 分析预期的异常

JUnit test — analysing expected Exceptions

本问题已经有最佳答案,请猛点这里访问。

在JUnit中,我目前正在使用注释来期待我的测试中的异常。

有没有办法分析这个例外? 例如,我期望CriticalServerException,但我也想验证getMessage方法的内容。


如果您有JUnit 4.7或更高版本,请尝试ExpectedException

这个问题有一个例子,复制如下:

1
2
3
4
5
6
7
8
9
@Rule
public ExpectedException exception = ExpectedException.none();

@Test
public void testRodneCisloRok(){
    exception.expect(IllegalArgumentException.class);
    exception.expectMessage("error1");
    new RodneCislo("891415",dopocitej("891415"));
}


我不确定你是否应该这样做。使用try-catch块来检查错误消息是如此junit3ish。我们现在有这个很酷的功能,您可以编写@Test(expected=CriticalServerException.class)并且想要"返回"并再次使用try-catch来获取您期望的异常,只是为了检查错误消息?

IMO你应该留下@Test(expected=CriticalServerException.class)注释并忽略错误信息。检查错误消息,可以更改,因为它是一个更"人类可读"的字符串而不是技术值,也可能是棘手的。您强制异常具有特定的错误消息,但您可能不知道是谁生成了异常以及他选择了什么错误消息。

通常,您希望测试方法是否抛出异常,而不是实际的错误消息。如果错误消息非常重要,您应该考虑使用它抛出的异常的子类并在@Test(expected=...)中检查它。


1
2
3
4
5
6
7
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());
}


如果要测试许多测试用例,请使用MethodRule作为常用解决方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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
                }
            }
        };    
    }
}

然后将规则用于您的测试类:

1
@Rule public ExceptionRule rule = new ExceptionRule();

1
2
3
4
5
6
try {
    // test code invacation
    fail("Exception not throw!!!");
} catch(CriticalServerException ex) {
    assertTrue("Invalid exception data", ex.toString().contains("error text"));
}

1
2
3
4
5
6
7
8
9
10
try
{
    // your code

    fail("Didn't throw expected exception");
}
catch(CriticalServerException e)
{
    assertEquals("Expected message", e.getMessage());
}


我不认为有一种方法可以使用注释。您可能必须回退到try-catch方式,在catch块中您可以验证消息


Java 8解决方案

这是我写的一个实用函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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.
}

(摘自我的博客)

使用方法如下:

1
2
3
4
5
6
7
8
9
@Test
public void testThrows()
{
    RuntimeException e = expectException( RuntimeException.class, () ->
        {
            throw new RuntimeException("fail!" );
        } );
    assert e.getMessage().equals("fail!" );
}

此外,如果您想阅读一些您不希望查看异常消息的原因,请参阅:https://softwareengineering.stackexchange.com/a/278958/41811


如果您想将消息与异常类型进行比较,那么您可以尝试下面的代码片段。

1
2
3
4
5
6
    @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

注意:这适用于junit 4.9以后。


看看流畅的异常规则,它"结合了Junit ExpectedException规则和AssertJ的断言方便性。"

1
2
3
4
5
6
7
8
9
10
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();
}

使用catch-exception:

1
2
3
catchException(obj).doSomethingCritical();
assertTrue(caughtException() instanceof CriticalServerException);
assertEquals("Expected Message", caughtException().getMessage());