這些方法都有多個(gè)重載方法,可以提供額外的消息來(lái)作為斷言不滿(mǎn)足時(shí)的提示消息,還可以接受 Java 8 中的 Supplier 接口來(lái)獲取要判斷的值和顯示的消息。清單 4 中給出了簡(jiǎn)單斷言的使用示例。
清單 4. 簡(jiǎn)單斷言
@Test
@DisplayName("simple assertion")
public void simple() {
assertEquals(3, 1 + 2, "simple math");
assertNotEquals(3, 1 + 1);
assertNotSame(new Object(), new Object());
Object obj = new Object();
assertSame(obj, obj);
assertFalse(1 > 2);
assertTrue(1 < 2);
assertNull(null);
assertNotNull(new Object());
}
第二類(lèi)是通過(guò) assertArrayEquals 方法來(lái)判斷兩個(gè)對(duì)象或原始類(lèi)型的數(shù)組是否相等,如清單 5 所示。
清單 5. assertArrayEquals 方法的示例
@Test
@DisplayName("array assertion")
public void array() {
assertArrayEquals(new int[]{1, 2}, new int[] {1, 2});
}
第三類(lèi)是通過(guò) assertAll 方法來(lái)判斷一組斷言是否滿(mǎn)足。assertAll 方法接受多個(gè) org.junit.jupiter.api.Executable 函數(shù)式接口的實(shí)例作為要驗(yàn)證的斷言,可以通過(guò) lambda 表達(dá)式很容易的提供這些斷言,如清單 6 所示。
清單 6. assertAll 方法的示例
@Test
@DisplayName("assert all")
public void all() {
assertAll("Math",
() -> assertEquals(2, 1 + 1),
() -> assertTrue(1 > 0)
);
}
第四類(lèi)是通過(guò) assertThrows 或 expectThrows 來(lái)判斷是否拋出期望的異常類(lèi)型。兩個(gè)方法的參數(shù)都是所期望的異常類(lèi)型和對(duì)應(yīng)的 Executable 接口的實(shí)現(xiàn)對(duì)象,區(qū)別在于 expectThrows 方法會(huì)返回拋出的異常對(duì)象。在清單 7 中,1/0 會(huì)拋出 ArithmeticException 異常,assertThrows 用來(lái)驗(yàn)證這一點(diǎn)。
清單 7. assertThrows 和 expectThrows 方法的示例
@Test
@DisplayName("throws exception")
public void exception() {
assertThrows(ArithmeticException.class, () -> System.out.println(1 / 0));
}
第五類(lèi)是 fail 方法,用來(lái)使一個(gè)測(cè)試方法失敗。清單 8 中的測(cè)試會(huì)直接失敗。
清單 8. 通過(guò) fail 方法直接使得測(cè)試失敗
@Test
@DisplayName("fail")
public void shouldFail() {
fail("This should fail");
}
JUnit 5 前置條件
JUnit 5 中的前置條件(assumptions)類(lèi)似于斷言,不同之處在于不滿(mǎn)足的斷言會(huì)使得測(cè)試方法失敗,而不滿(mǎn)足的前置條件只會(huì)使得測(cè)試方法的執(zhí)行終止。前置條件可以看成是測(cè)試方法執(zhí)行的前提,當(dāng)該前提不滿(mǎn)足時(shí),沒(méi)有繼續(xù)執(zhí)行的必要。在清單 9 中,assumeTrue 和 assumFalse 確保給定的條件為 true 或 false,不滿(mǎn)足條件會(huì)使得測(cè)試執(zhí)行終止。assumingThat 的參數(shù)是表示條件的布爾值和對(duì)應(yīng)的 Executable 接口的實(shí)現(xiàn)對(duì)象。只有條件滿(mǎn)足時(shí),Executable 對(duì)象才會(huì)被執(zhí)行;當(dāng)條件不滿(mǎn)足時(shí),測(cè)試執(zhí)行并不會(huì)終止。
清單 9. JUnit 5 前置條件
@DisplayName("Assumptions")
public class AssumptionsTest {
private final String environment = "DEV";
@Test
@DisplayName("simple")
public void simpleAssume() {
assumeTrue(Objects.equals(this.environment, "DEV"));
assumeFalse(() -> Objects.equals(this.environment, "PROD"));
}
@Test
@DisplayName("assume then do")
public void assumeThenDo() {
assumingThat(
Objects.equals(this.environment, "DEV"),
() -> System.out.println("In DEV")
);
}
}
嵌套測(cè)試
JUnit 5 可以通過(guò) Java 中的內(nèi)部類(lèi)和@Nested 注解實(shí)現(xiàn)嵌套測(cè)試,從而可以更好的把相關(guān)的測(cè)試方法組織在一起。在內(nèi)部類(lèi)中可以使用@BeforeEach 和@AfterEach 注解,而且嵌套的層次沒(méi)有限制。清單 10 中給出了使用嵌套測(cè)試的示例,用來(lái)測(cè)試 HashMap 的功能。
清單 10. 嵌套測(cè)試
@DisplayName("Nested tests for HashMap")
public class MapNestedTest {
Map<String, Object> map;
@Nested
@DisplayName("when a new")
class WhenNew {
@BeforeEach
void create() {
map = new HashMap<>();
}
@Test
@DisplayName("is empty")
void isEmpty() {
assertTrue(map.isEmpty());
}
@Nested
@DisplayName("after adding a new entry")
class AfterAdd {
String key = "key";
Object value = "value";
@BeforeEach
void add() {
map.put(key, value);
}
@Test
@DisplayName("is not empty")
void isNotEmpty() {
assertFalse(map.isEmpty());
}
@Test
@DisplayName("returns value when getting by key")
void returnValueWhenGettingByKey() {
assertEquals(value, map.get(key));
}
@Nested
@DisplayName("after removing the entry")
class AfterRemove {
@BeforeEach
void remove() {
map.remove(key);
}
@Test
@DisplayName("is empty now")
void isEmpty() {
assertTrue(map.isEmpty());
}
@Test
@DisplayName("returns null when getting by key")
void returnNullForKey() {
assertNull(map.get(key));
}
}
}
}
}
依賴(lài)注入
在 JUnit 5 之前,標(biāo)準(zhǔn)的測(cè)試類(lèi)和測(cè)試方法是不允許有額外的參數(shù)的。這個(gè)限制在 JUnit 5 被取消了。JUnit 5 除了提供內(nèi)置的標(biāo)準(zhǔn)參數(shù)之外,還可以通過(guò)擴(kuò)展機(jī)制來(lái)支持額外的參數(shù)。
當(dāng)參數(shù)的類(lèi)型是 org.junit.jupiter.api.TestInfo 時(shí),JUnit 5 會(huì)在運(yùn)行測(cè)試時(shí)提供一個(gè) TestInfo 接口的對(duì)象。通過(guò) TestInfo 接口,可以獲取到當(dāng)前測(cè)試的相關(guān)信息,包括顯示名稱(chēng)、標(biāo)簽、測(cè)試類(lèi)和測(cè)試方法,如清單 11 所示。
清單 11. TestInfo 依賴(lài)注入
@Test
@DisplayName("test info")
public void testInfo(final TestInfo testInfo) {
System.out.println(testInfo.getDisplayName());
}
當(dāng)參數(shù)的類(lèi)型是 org.junit.jupiter.api.TestReporter 時(shí),在運(yùn)行測(cè)試時(shí),通過(guò)作為參數(shù)傳入的 TestReporter 接口對(duì)象,來(lái)輸出額外的鍵值對(duì)信息。這些信息可以被測(cè)試執(zhí)行的監(jiān)聽(tīng)器 TestExecutionListener 處理,也可以被輸出到測(cè)試結(jié)果報(bào)告中,如清單 12 所示。
清單 12. TestReporter 依賴(lài)注入
@Test
@DisplayName("test reporter")
public void testReporter(final TestReporter testReporter) {
testReporter.publishEntry("name", "Alex");
}
除了 TestInfo 和 TestReporter 之外,也可以通過(guò) JUnit 5 的擴(kuò)展機(jī)制來(lái)添加對(duì)其他類(lèi)型參數(shù)的支持。將在下面關(guān)于 JUnit 5 擴(kuò)展機(jī)制的一節(jié)中進(jìn)行介紹。