簡體   English   中英

JUnit 5:如何斷言拋出異常?

[英]JUnit 5: How to assert an exception is thrown?

JUnit 5中有沒有更好的斷言方法拋出異常的方法?

目前,我必須使用 @Rule 來驗證我的測試是否拋出異常,但這不適用於我希望多個方法在我的測試中拋出異常的情況。

您可以使用assertThrows() ,它允許您在同一個測試中測試多個異常。 在 Java 8 中支持 lambda,這是在 JUnit 中測試異常的規范方法。

根據JUnit 文檔

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)

斷言提供的可執行文件的執行引發了預期類型的​​異常並返回該異常。

如果沒有拋出異常,或者拋出了不同類型的異常,則此方法將失敗。

如果您不想對異常實例執行額外檢查,只需忽略返回值。

@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 中對其進行了更改(預期:InvalidArgumentException,實際:調用方法),代碼如下所示:

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

現在 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 的示例: 斷言異常代碼示例

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

TL;DR:如果您使用的是 JUnit 5.8.0+ 版本,則可以使用assertThrowsExactly()而不是assertThrows()來匹配確切的異常類型。

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

您可以使用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());
}

我認為這是一個更簡單的例子

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

對包含空ArrayList的可選項調用get()將引發NoSuchElementException assertThrows聲明了預期的異常並提供了一個 lambda 供應商(不接受任何參數並返回一個值)。

感謝@prime 的回答,我希望能詳細說明。

一個更簡單的班輪。 此示例使用 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()
}

實際上,我認為此特定示例的文檔中有錯誤。 預期的方法是 expectThrows

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

我的解決方案:

    protected <T extends Throwable> void assertExpectedException(ThrowingRunnable methodExpectedToFail, Class<T> expectedThrowableClass,
        String expectedMessage) {
    T exception = assertThrows(expectedThrowableClass, methodExpectedToFail);
    assertEquals(expectedMessage, exception.getMessage());
}

你可以這樣稱呼它:

    assertExpectedException(() -> {
        carService.findById(id);
    }, IllegalArgumentException.class, "invalid id");
This is what I do when testing to make sure an exception has been thrown

    
    //when
    final var tripConsumer = new BusTripConsumer(inputStream);
    final Executable executable = () -> tripConsumer.deserialiseTripData();

    //then
    assertThrows(IllegalArgumentException.class, executable);

有三種方法可以在Junit中聲明某個異常。 讓我們為它編寫單元測試用例。

1. try-catch成語這個成語是最流行的成語之一,因為它已經在JUnit 3中使用過。這種方法是一種常見的模式。 當沒有拋出異常並且在catch子句中驗證異常本身時,測試將失敗。

@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。 這被認為是一種不好的做法,因為你的代碼可能會在實際預期的其他地方拋出異常而你的測試仍然會通過!

這種方法的缺點之一是您無法斷言異常消息。

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

3. Junit @Rule可以使用ExceptedException規則創建相同的示例。 規則必須是標有@Rule注釋的公共字段。

    @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注釋。

這是一個簡單的方法。

@Test
void exceptionTest() {

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

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

}

只有當您期望的異常被拋出時,它才會成功。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM