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

Posted

技术标签:

【中文标题】JUnit 5:如何断言抛出异常?【英文标题】:JUnit 5: How to assert an exception is thrown? 【发布时间】:2017-03-09 04:24:42 【问题描述】:

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

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

【问题讨论】:

你可能有兴趣检查 AssertJ 以检查异常,因为它比 JUnit5 更灵活 这是一个在 JUnit4 和 JUnit5 中 how assert that an exception is Thrown 的好例子 如果您期望多个方法在一个测试中抛出异常,那就是代码异味;你可能想编写多个测试,每个抛出一个异常 【参考方案1】:

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

根据JUnit docs:

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"));

【讨论】:

来自一个老派的“我对 Junit5 了解不多,对 Java8 可能还不够了解”……这看起来很奇怪。您介意添加更多解释吗?比如“其中的哪一部分是正在测试的实际‘生产代码’……应该抛出的”? () -> points 指向一个接受零参数的 lambda 表达式。因此,预期会引发异常的“生产代码”位于指向的代码块中(即大括号内的throw new... 语句)。 通常 lambda 表达式会与被测对象 (SUT) 交互。换句话说,像上面这样直接抛出异常只是为了演示目的。 从 5.0.0-M4 版开始,expectThrows 不再可用。只允许 assertThrows。请参阅github.com/junit-team/junit5/blob/master/documentation/src/docs/…:'删除了已弃用的 Assertions.expectThrows() 方法以支持 Assertions.assertThrows()' 木星太蹩脚了,迫使我们以这种方式断言信息:-/【参考方案2】:

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

public static T assertThrows(Class expectedType, 可执行文件)

断言执行所提供的可执行文件会引发预期类型的​​异常并返回异常。

如果没有抛出异常,或者抛出了不同类型的异常,这个方法就会失败。

如果您不想对异常实例执行额外检查,只需忽略返回值即可。

@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 的最新版本。此外,如果 Lambda 只有一行,IntelliJ 会将 lambda 进一步压缩:assertThrows(NoSuchElementException.class, myLinkedList::getFirst); @anon58192932 这不会发生,因为只有一行,这是因为您只调用一种方法(尽管这自然也只会自动成为一行,除非您有一个长达一英里的方法姓名)。换句话说:object -> object.methodCall() 可以替换为 Object::methodCall,但您不能将 object -> object.getOneThing().getAnotherThing().doSomething() 替换为方法调用。 感谢 Assertions.assertThrows()。【参考方案3】:

他们在 JUnit 5 中对其进行了更改(预期:InvalidArgumentException,实际:调用方法),代码如下所示:

@Test
public void wrongInput() 
    Throwable exception = assertThrows(InvalidArgumentException.class,
            ()->objectName.yourMethod("WRONG"); );

【讨论】:

【参考方案4】:

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

您可以同时测试一般异常和自定义异常

一般异常情况:

ExpectGeneralException.java

public void validateParameters(Integer param ) 
    if (param == null) 
        throw new NullPointerException("Null parameters are not allowed");
    

ExpectGeneralExceptionTest.java

@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 的示例:assert exception code sample

ExpectCustomException.java

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

@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());

【讨论】:

JUnit 处理内置异常和自定义异常的方式没有区别。【参考方案5】:

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

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());

【讨论】:

【参考方案6】:

TL;DR:如果您使用的是 JUnit 5.8.0+ 版本,则可以使用 assertThrowsExactly() 来匹配确切的异常类型。

assertThrowsExactly(FileNotFoundException.class, () -> service.blah());

您可以使用assertThrows(),但使用assertThrows,即使抛出的异常是子类型,您的断言也会通过。

这是因为,JUnit 5 通过调用 Class.isIntance(..) 来检查异常类型,即使抛出的异常是子类型,Class.isInstance(..) 也会返回 true。

解决方法是在 Class 上断言:

Throwable throwable =  assertThrows(Throwable.class, () -> 
    service.readFile("sampleFile.txt");
);
assertEquals(FileNotFoundException.class, throwable.getClass());

【讨论】:

【参考方案7】:

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

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

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

感谢@prime 的回答,我希望能详细说明。

【讨论】:

方法assertThrows返回抛出的异常。所以你可以像NoSuchElementException e = assertThrows(NoSuchElementException.class, () -&gt; opt2.get()); 那样做,然后你可以在下面对你想要的异常对象做任何类型的断言。【参考方案8】:

更简单的一个班轮。此示例使用 Java 8 和 JUnit 5 不需要 lambda 表达式或花括号

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

@Test
void exceptionTesting() 

    assertThrows(MyException.class, myStackObject::doStackAction, "custom message if assertion fails..."); 

// note, no parenthesis on doStackAction ex ::pop NOT ::pop()

【讨论】:

【参考方案9】:

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

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

【讨论】:

"删除了已弃用的 Assertions.expectThrows() 方法,取而代之的是 Assertions.assertThrows()。" 对于 Junit 5,确保它来自 org.junit.jupiter.api.Assertions 而不是 org.testng.Assert。我们的项目同时包含 Junit 和 TestNG,并且我一直收到 assertThrows 返回 void 错误,直到我将其更改为 assertExpects。原来我用的是org.testng.Assert。【参考方案10】:

这是一个简单的方法。

@Test
void exceptionTest() 

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

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


只有当你期望的异常被抛出时才会成功。

【讨论】:

以上是关于JUnit 5:如何断言抛出异常?的主要内容,如果未能解决你的问题,请参考以下文章

JUnit 4 如何正确测试异常

如何使用 JUnit Test 注释断言我的异常消息?

断言与JUnit断言

Junit中的异常测试

Java JUnit pass/failure/error区别

在Java中断言异常,如何? [复制]