Junit5系列-Junit5中assertThrows()與assertDoesNotThrow()方法詳解

yoylee_web發表於2019-01-15

系列導航

點選跳轉到系列博文目錄導航

簡介

Junit5中新新增了對方法丟擲異常的斷言Assertions類中的assertThrows()和assertDoesNotThrow(),使用此方法可以對被測試方法丟擲的異常進行斷言測試,而在junit4中的對異常進行斷言實現相對來說比較麻煩的。

  • assertThrows()主要對被測試方法的丟擲異常進行測試,測試所丟擲的異常是否滿足預期。
  • assertDoesNotThrow()主要用來判定被測試方法是否丟擲了異常,如果丟擲異常則斷言失敗,無異常丟擲則斷言成功。

下面來看一些案例使用:

案例分析

其中每個方法的作用程式碼中的註釋寫的已經非常清楚了,就不再贅述了.

大家最好將程式碼自己測試一遍,可以加深理解與記憶!

案例程式碼:

1.使用到的被測試方法

public class AssertTestModel {
    public static void testThrowArithmeticException(int numA,int numB){
        try {
            int result = numA/numB;
        }catch (ArithmeticException e){
            throw new ArithmeticException("The numB not allowed to '0'!!");
        }
    }
}

2.測試案例

import cn.yoylee.junit5.AssertTestModel;
import org.junit.jupiter.api.Test;

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

/**
 * @author liyangyang
 * @date 2019/1/14
 */
class AssertThrowsTest {

    @Test
    void testAssertThrows(){
        //2:如果丟擲的異常與設定的異常相同,則這一步的斷言成功並返回一個異常的頂級父類
        Throwable exception = assertThrows(ArithmeticException.class,()->{
            //1:執行此語句會丟擲一個ArithmeticException異常,被assertThrows方法捕獲
            AssertTestModel.testThrowArithmeticException(2,0);
        });
        //3:接下來便可以對返回的異常進行一些其他的測試
        //比如對異常的資訊進行斷言測試等。。
        assertEquals("The numB not allowed to '0'!!",exception.getMessage());
    }

    @Test
    void testAssertDoesNotThrow1(){
        //因為函數語言程式設計中的執行語句丟擲了異常,所以斷言失敗
        assertDoesNotThrow(()->{
            AssertTestModel.testThrowArithmeticException(2,0);
        });
    }

    @Test
    void testAssertDoesNotThrow2(){
        //斷言成功
        assertDoesNotThrow(()->{
            AssertTestModel.testThrowArithmeticException(2,1);
        });
    }
    
    @Test
    void testtestAssertDoesSNotThrowHaveReturn(){
        //這是帶返回引數的assertDoesNotThrow方法,在沒有異常丟擲的情況下會返回一個值
        int re =  assertDoesNotThrow(() -> {
            int result = AssertTestModel.testThrowArithmeticException(2,1);
            return result;
        });

        //其實上面的語句可以簡化為下面的語句不過為了便於理解,還是使用的上述表達
//        int re =  assertDoesNotThrow(() -> AssertTestModel.testThrowArithmeticException(2,1));

        //可以對返回的結果值進行一些測試等
        assertEquals(re,2);
    }
}

到這裡大家應該已經知道如何使用這兩個方法了,但是其執行過程是怎樣的,怎樣設計的呢?我們接下來看一下

原始碼分析

首先,assertThrows有三個過載方法:

    public static <T extends Throwable> T assertThrows(Class<T> expectedType, Executable executable) {
        return AssertThrows.assertThrows(expectedType, executable);
    }

    public static <T extends Throwable> T assertThrows(Class<T> expectedType, Executable executable, String message) {
        return AssertThrows.assertThrows(expectedType, executable, message);
    }

    public static <T extends Throwable> T assertThrows(Class<T> expectedType, Executable executable, Supplier<String> messageSupplier) {
        return AssertThrows.assertThrows(expectedType, executable, messageSupplier);
    }

我們可以看到,其中都是呼叫了AssertThrows.assertThrows方法,這裡的AssertThrows和Assertions的關係和我們在Junit5系列-Junit5中Assertions斷言類一文中Assertions和assertTrue關係一樣。
接下來就看一下AssertThrows.assertThrows()到底是何許人也:

private static <T extends Throwable> T assertThrows(Class<T> expectedType, Executable executable, Object messageOrSupplier) {
        try {
            //執行引數executable中設定的語句
            executable.execute();
        } catch (Throwable var5) {
            //判斷捕獲的異常是否和自定義的異常類相同,相同則返回異常物件
            if (expectedType.isInstance(var5)) {
                return var5;
            }
            
            //如果捕獲的異常和自定義的異常不同,通過自定義的列印資訊messageOrSupplier組裝異常並丟擲(AssertionFailedError為Junit5的自定義異常)
            String message = AssertionUtils.buildPrefix(AssertionUtils.nullSafeGet(messageOrSupplier)) + AssertionUtils.format(expectedType, var5.getClass(), "Unexpected exception type thrown");
            throw new AssertionFailedError(message, var5);
        }
        //如果執行的語句沒有異常,組裝“沒有異常的”異常丟擲
        String message = AssertionUtils.buildPrefix(AssertionUtils.nullSafeGet(messageOrSupplier)) + String.format("Expected %s to be thrown, but nothing was thrown.", AssertionUtils.getCanonicalName(expectedType));
        throw new AssertionFailedError(message);
}

我在原始碼中註釋了其執行過程,相對來說是比較簡單的,這裡的引數引數介紹一下:

  • 返回型別:<T extends Throwable>,繼承自Throwable類的子型別,必須是繼承Throwable的子類。所以我們在測試方法中可以直接使用Throwable接受返回值。
  • 引數:Class<T> expectedType,定義想要測試的異常型別
  • 引數:Object messageOrSupplier,這裡Object型別的原因是可能會接受String型別的自定義資訊型別,還有可能接受函式式介面Supplier的自定義資訊。
  • 引數:Executable executable,函式式介面,可以使用lambda表示式,可以稍微看一下其原始碼:
@FunctionalInterface
@API(
    status = Status.STABLE,
    since = "5.0"
)
public interface Executable {
    void execute() throws Throwable;
}

其中@FunctionalInterface可以看出是一個函式式介面,無參無返回值的呼叫方法。

好了,到這裡大家應該對assertThrows方法有個全面的瞭解,對於的assertDoseNotThrows方法來說,實現方式類似,有6個過載方法,3個有返回值的3個無返回值的過載, 最後都是呼叫的AssertDoesNotThrow.assertDoesNotThrow()方法實現的,其原始碼為:

//泛型返回值
private static <T> T assertDoesNotThrow(ThrowingSupplier<T> supplier, Object messageOrSupplier) {
        try {
            //函式式介面方法執行,無異常正常返回
            return supplier.get();
        } catch (Throwable var4) {
            //捕獲異常則根據messageOrSupplier自定義訊息組裝異常並丟擲
            String message = AssertionUtils.buildPrefix(AssertionUtils.nullSafeGet(messageOrSupplier)) + "Unexpected exception thrown: " + var4.getClass().getName();
            throw new AssertionFailedError(message, var4);
        }
 }

如果轉載此博文,請附上本文連結,謝謝合作~ :https://blog.csdn.net/csdn___lyy

如果感覺這篇文章對您有所幫助,請點選一下“喜歡”或者“關注”博主,您的喜歡和關注將是我前進的最大動力!

相關文章