Junit5系列-Junit5中Assertions斷言類

yoylee_web發表於2019-01-08

系列導航

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

簡介

junit5中的JUnit Jupiter提供了Assertions類來替代了junit4中的Assert類並且新增了一些新的方法,所以工作過程中完全可以使用Assertions代替Assert類。
其包名稱為:org.junit.jupiter.api.Assertions
Assertions中提供的方法都是靜態方法,我們可以通過import靜態資源進行使用,例如:

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

當然上述是匯入了所有的方法,單獨匯入也可。

案例解析

下面對Assertions進行一個簡單的應用
程式碼的註釋都很清楚,就不再多說了

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

import static java.time.Duration.ofMillis;
import static java.time.Duration.ofMinutes;
import static org.junit.jupiter.api.Assertions.*;
/**
 * @author liyangyang
 * @date 2019/1/8
 */
class AssertionsDemo {
    //定義一個person物件,Person類裡面有兩個引數lastName,firstName
    static Person person = new Person();

    /**
     * 使用 @BeforeAll註解在所有測試方法執行前執行person物件的賦值
     */
    @BeforeAll
    static void initPerson(){
        person.setFirstName("John");
        person.setLastName("Doe");
    }

    /**
     * assertEquals比較兩個值是否相同
     * assertTrue 判斷括號裡面的引數是否為true
     */
    @Test
    void standardAssertions() {
        assertEquals(2, 2);
        //當不相等時,會列印出第三個引數,下面的所有的此型別的引數都是這種作用
        assertEquals(4, 5, "The optional assertion message is now the last parameter.");
        assertTrue('a' < 'b', "Assertion messages can be lazily evaluated -- "
                + "to avoid constructing complex messages unnecessarily.");
    }

    /**
     * assertAll()方法用於將多個測試語句放在一個組中執行
     * 組中若有一個測試語句不通過,則這個組將會一起報錯.
     * 方法中第一個引數:組名稱
     * 方法中第二個引數:組測試語句
     */
    @Test
    void groupedAssertions() {
        assertAll("person",
                () -> assertEquals("John", person.getFirstName()),
                () -> assertEquals("Doe", person.getLastName())
        );
    }

    /**
     * assertAll()方法也可以巢狀多個assertAll()方法
     * 其中巢狀的多個測試組,這些組只會列印出這個組和父組的錯誤,對其他的組沒有影響
     */
    @Test
    void dependentAssertions() {
        assertAll("properties",
                //第一個測試組
                () -> {
                    String firstName = person.getFirstName();
                    assertNotNull(firstName);

                    assertAll("first name",
                            () -> assertTrue(firstName.startsWith("J")),
                            () -> assertTrue(firstName.endsWith("n"))
                    );
                },
                //第二個測試組
                () -> {
                    String lastName = person.getLastName();
                    assertNotNull(lastName);

                    assertAll("last name",
                            () -> assertTrue(lastName.startsWith("D")),
                            () -> assertTrue(lastName.endsWith("e"))
                    );
                }
        );
    }

    /**
     * assertThrows()可以用來判斷lambda表示式中的程式碼丟擲的異常
     * 比如下面案例就是測試了丟擲異常的資訊是否相同
     * 引數:
     * 1:異常類宣告
     * 2:測試程式碼Lambda表示式
     */
    @Test
    void exceptionTesting() {
        Throwable exception = assertThrows(IllegalArgumentException.class, () -> {
            try {
                //這裡只是簡單的做個測試,當然1/0不該拋IllegalArgumentException異常 ,只是簡單的測試一下
              int s = 1/0;
            }catch (Exception e){
                throw new IllegalArgumentException("a message");
            }
        });
        assertEquals("a message", exception.getMessage());
    }

    /**
     * assertTimeout()對方法執行時間進行測試
     * 這裡要藉助java.time.Duration中的方法結合實現
     * 例項中執行的程式碼部分必須在2分鐘之內執行完畢,否則測試不通過
     */
    @Test
    void timeoutNotExceeded() {
        assertTimeout(ofMinutes(2), () -> {
            //執行的程式碼部分
        });
    }

    /**
     * assertTimeout()還可以接受一個返回值(泛型 T)
     * 被測試程式碼如果通過測試並返回一個值,這個值被assertTimeout()方法返回
     */
    @Test
    void timeoutNotExceededWithResult() {
        String actualResult = assertTimeout(ofMinutes(2), () -> {
            return "a result";
        });
        assertEquals("a result", actualResult);
    }

    /**
     * assertTimeout()毫秒案例
     */
    @Test
    void timeoutExceeded() {
        assertTimeout(ofMillis(10), () -> {
            Thread.sleep(100);
        });
    }
}

這裡只是介紹一下其用法,其中還有好多方法沒涉及到,者要等我們在使用的過程中再去查詢合適的方法。

相信通過上述案例你應該對Assertions有個大體的瞭解, 下面再細說一下。

原始碼解析

因為程式碼太長了,就不貼程式碼了,大家有需要可以自行檢視。
裡面有好多靜態測試方法,每個方法又有各自的過載。
在這裡插入圖片描述
這只是擷取了不到三分之一的方法。

下面通過介紹public static void assertNull(Object actual, String message)這個判斷物件為空的方法,來大致的看一下其實現過程。

  1. Assertions中的assertNull方法原始碼
 public static void assertNull(Object actual, String message) {
        AssertNull.assertNull(actual, message);
}

呼叫了AssertNull類中的assertNull方法

  1. AssertNull類中的assertNull方法原始碼
static void assertNull(Object actual, String message) {
        if (actual != null) {
            failNotNull(actual, message);
        }

}
  • 判斷引數actual是否為null,若為null則表示測試成功,方法結束,不報錯。
  • 如果引數actual不為空,則要呼叫failNotNull方法
  1. failNotNull方法原始碼
private static void failNotNull(Object actual, String message) {
        AssertionUtils.fail(AssertionUtils.buildPrefix(message) + "expected: <null> but was: <" + actual + ">", (Object)null, actual);
}

這個方法主要就是組裝了錯誤訊息,並將被驗證引數傳遞到下一層

  1. fail方法原始碼
static void fail(String message, Object expected, Object actual) {
        throw new AssertionFailedError(message, expected, actual);
}

在這裡直接丟擲了一個自定義異常。
到這裡一個測試的過程就幾乎結束了,大體來說不難,大家如果還想了解其他的方法原始碼,可以自己debug跟蹤原始碼來檢視。

  1. 最後簡單的看一下這個自定義異常AssertionFailedError吧
    其繼承關係為:
public class AssertionFailedError extends AssertionError
--->
public class AssertionError extends Error
--->
public class Error extends Throwable

可以看書這是正常的自定義異常的模式,繼承JDK自帶的java.long.error類,實現自己的Error類,這裡的AssertionError是所有Assertions類中方法報錯的父類,其他特定的錯誤類就是繼承的此類。

Assertions與AssertNull

另外,在上述過程中我們知道,Assertions.assertNull()其實就是呼叫的AssertNull中的assertNull()實現的,我們看一下AssertNull的原始碼:

class AssertNull {
    private AssertNull() {
    }
    //包可見靜態方法
    static void assertNull(Object actual) {
        assertNull(actual, (String)null);
    }
    //包可見靜態方法
    static void assertNull(Object actual, String message) {
        if (actual != null) {
            failNotNull(actual, message);
        }

    }
    //包可見靜態方法
    static void assertNull(Object actual, Supplier<String> messageSupplier) {
        if (actual != null) {
            failNotNull(actual, AssertionUtils.nullSafeGet(messageSupplier));
        }

    }
    //類內呼叫方法
    private static void failNotNull(Object actual, String message) {
        AssertionUtils.fail(AssertionUtils.buildPrefix(message) + "expected: <null> but was: <" + actual + ">", (Object)null, actual);
    }
}

再看一下這兩個類是不是在一個包中:
在這裡插入圖片描述
從原始碼和圖片中不難發現,AssertNull中其實和Assertions一樣也是提供了包內可見的靜態方法 ,所以我們也可以通過引入import static org.junit.jupiter.api.AssertNull.*;來直接使用AssertNull中的方法,不過這可不是個好想法。

Assertions將測試方法集中在該方法內,不僅便於管理,還減少程式碼的複雜度,效能沒有任何影響,為什麼要去直接使用AssertNull等類呢,嘿嘿

最後:當然,Assertions提供的斷言方法雖然多,但有時候還是不夠的,當Junit5不支援你的單元測試需求時,Junit5官方還是鼓勵程式設計師去使用第三方支援庫的,比如:AssertJ、Hamcrest,Truth等。。。。

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

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

refer: 官網

我的部落格即將同步至騰訊雲+社群,邀請大家一同入駐:https://cloud.tencent.com/developer/support-plan?invite_code=2gwn5c4zib6s0

相關文章