关于java:JUnit 5:如何断言异常抛出?

JUnit 5: How to assert an exception is thrown?

有没有更好的方法断言方法在JUnit 5中抛出异常?

目前,我必须使用@Rule来验证我的测试是否会引发异常,但这对于我希望多个方法在我的测试中抛出异常的情况不起作用。


您可以使用assertThrows(),它允许您在同一测试中测试多个异常。在Java 8中支持lambdas,这是在JUnit中测试异常的规范方法。

根据JUnit文档:

1
2
3
4
5
6
7
8
9
10
11
import static org.junit.jupiter.api.Assertions.assertThrows;

@Test
void exceptionTesting() {
    MyException thrown =
        assertThrows(MyException.class,
           () -> myObject.doThing(),
          "Expected doThing() to throw, but it didn't");

    assertTrue(thrown.getMessage().contains("Stuff"));
}


在Java 8和JUnit 5(Jupiter)中,我们可以声明异常,如下所示。
使用org.junit.jupiter.api.Assertions.assertThrows

public static < T extends Throwable > T assertThrows(Class< T > expectedType,
Executable executable)

Asserts that execution of the supplied executable throws an exception of the expectedType and returns the exception.

If no exception is thrown, or if an exception of a different type is thrown, this method will fail.

If you do not want to perform additional checks on the exception instance, simply ignore the return value.

1
2
3
4
5
6
7
8
@Test
public void itShouldThrowNullPointerExceptionWhenBlahBlah() {
    assertThrows(NullPointerException.class,
            ()->{
            //do whatever you want to do here
            //ex : objectName.thisMethodShoulThrowNullPointerExceptionForNullParameter(null);
            });
}

该方法将使用org.junit.jupiter.api中的功能接口Executable

参考:

  • http://junit.org/junit5/docs/current/user-guide/#writing-tests-assertions
  • http://junit.org/junit5/docs/5.0.0-M2/api/org/junit/jupiter/api/Executable.html
  • http://junit.org/junit5/docs/5.0.0-M4/api/org/junit/jupiter/api/Assertions.html#assertThrows-java.lang.Class-org.junit.jupiter.api.function。 Executable-


他们在JUnit 5中更改了它(预期:InvalidArgumentException,actual:invoked method),代码如下所示:

1
2
3
4
5
@Test
public void wrongInput() {
    Throwable exception = assertThrows(InvalidArgumentException.class,
            ()->{objectName.yourMethod("WRONG");} );
}

现在,Junit5提供了一种断言异常的方法

您可以测试一般例外和自定义例外

一般异常情况:

ExpectGeneralException.java

1
2
3
4
5
public void validateParameters(Integer param ) {
    if (param == null) {
        throw new NullPointerException("Null parameters are not allowed");
    }
}

ExpectGeneralExceptionTest.java

1
2
3
4
5
6
7
8
9
10
@Test
@DisplayName("Test assert NullPointerException")
void testGeneralException(TestInfo testInfo) {
    final ExpectGeneralException generalEx = new ExpectGeneralException();

     NullPointerException exception = assertThrows(NullPointerException.class, () -> {
            generalEx.validateParameters(null);
        });
    assertEquals("Null parameters are not allowed", exception.getMessage());
}

您可以在此处找到测试CustomException的示例:断言异常代码示例

ExpectCustomException.java

1
2
3
4
5
6
7
8
9
10
11
public String constructErrorMessage(String... args) throws InvalidParameterCountException {
    if(args.length!=3) {
        throw new InvalidParameterCountException("Invalid parametercount: expected=3, passed="+args.length);
    }else {
        String message ="";
        for(String arg: args) {
            message += arg;
        }
        return message;
    }
}

ExpectCustomExceptionTest.java

1
2
3
4
5
6
7
8
9
10
@Test
@DisplayName("Test assert exception")
void testCustomException(TestInfo testInfo) {
    final ExpectCustomException expectEx = new ExpectCustomException();

     InvalidParameterCountException exception = assertThrows(InvalidParameterCountException.class, () -> {
            expectEx.constructErrorMessage("sample","error");
        });
    assertEquals("Invalid parametercount: expected=3, passed=2", exception.getMessage());
}

我认为这是一个更简单的例子

1
2
3
List<String> emptyList = new ArrayList<>();
Optional<String> opt2 = emptyList.stream().findFirst();
assertThrows(NoSuchElementException.class, () -> opt2.get());

在包含空ArrayList的可选项上调用get()将抛出NoSuchElementExceptionassertThrows声明预期的异常并提供lambda供应商(不接受任何参数并返回值)。

感谢@prime的回答,我希望能够详细阐述。


您可以使用assertThrows()。我的例子取自文档http://junit.org/junit5/docs/current/user-guide/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

....

@Test
void exceptionTesting() {
    Throwable exception = assertThrows(IllegalArgumentException.class, () -> {
        throw new IllegalArgumentException("a message");
    });
    assertEquals("a message", exception.getMessage());
}


实际上我认为这个特定示例的文档中存在错误。预期的方法是expectThrows

1
2
public static void assertThrows(
public static <T extends Throwable> T expectThrows(


这是一个简单的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Test
void exceptionTest() {

   try{
        model.someMethod("invalidInput");
        fail("Exception Expected!");
   }
   catch(SpecificException e){

        assertTrue(true);
   }
   catch(Exception e){
        fail("wrong exception thrown");
   }

}

只有在抛出您期望的异常时才会成功。


有三种方法可以在Junit中声明某个异常。让我们为它编写单元测试用例。

试试成语
这个成语是最受欢迎的成语之一,因为它已经在JUnit 3中使用过。这种方法是一种常见的模式。当没有抛出异常并且在catch子句中验证异常本身时,测试将失败。

1
2
3
4
5
6
7
8
9
10
11
@Test
public void convertIntoUpperCase_withInvalidInput_tryCatchIdiom() {
    try {
        exceptionHandling.convertIntoUpperCase("");
        fail("It should throw IllegalArgumentException");
    } catch (IllegalArgumentException e) {
        Assertions.assertThat(e)
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessage("Empty value is passed.");
    }
}

2. @Test预期注释
在这种方法中,我们在@Test中指定预期的异常,如下所示
@Test(expected = IllegalArgumentException.class)

当没有抛出异常时,您将收到以下消息:java.lang.AssertionError:预期的异常:java.lang.IllegalArgumentException

使用这种方法,你需要小心。有时候很容易期待一般的Exception,RuntimeException甚至是Throwable。这被认为是一种不好的做法,因为你的代码可能会在实际预期的其他地方抛出异常而你的测试仍然会通过!

这种方法的缺点之一是您无法断言异常消息。

1
2
3
4
@Test(expected = IllegalArgumentException.class)
public void convertIntoUpperCase_withInvalidInput_testExpected() {
    exceptionHandling.convertIntoUpperCase("");
}

3. Junit @Rule
可以使用ExceptedException规则创建相同的示例。规则必须是标有@Rule注释的公共字段。

1
2
3
4
5
6
    @Test
    public void convertIntoUpperCase_withInvalidInput_ExpectedExceptionRule() {
        exception.expect(IllegalArgumentException.class);
        exception.expectMessage("Empty value is passed.");
        exceptionHandling.convertIntoUpperCase("");
    }

我发现上面的代码更具可读性,因此我更喜欢使用这种方法。

当没有抛出异常时,您将收到以下消息:java.lang.AssertionError:要抛出的预期测试(java.lang.IllegalArgumentException和异常的实例,并带有消息"传递空值。")。挺棒的。

但不是我用上述方法检查的所有例外情况。有时我只需检查抛出的异常类型,然后使用@Test注释。