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

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

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

【问题讨论】:

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

标签: java junit junit5 junit-jupiter


【解决方案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

参考:

【讨论】:

  • 用这个登上顶峰!这是迄今为止最好的答案,它是 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");
             }
          
          }
          

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2019-09-03
            • 2011-08-06
            • 1970-01-01
            • 2019-01-28
            • 2022-06-29
            • 2023-03-15
            • 1970-01-01
            相关资源
            最近更新 更多