對(duì)于所有的 Java 開(kāi)發(fā)人員來(lái)說(shuō),你可以沒(méi)有聽(tīng)說(shuō)過(guò) Spring 或是 Hibernate 框架,但是一定聽(tīng)說(shuō)過(guò) JUnit。JUnit 作為 Java 單元測(cè)試的鼻祖與事實(shí)上的標(biāo)準(zhǔn),在非常多的項(xiàng)目中被使用。即便新興的單元測(cè)試框架,如 TestNG 等,不斷出現(xiàn),JUnit 的重要性仍然是不言而喻的。目前廣泛使用的是 JUnit 4 版本,而 JUnit 即將迎來(lái)它的新版本 JUnit 5。JUnit 5 在增加了很多的新特性的同時(shí),又保持了對(duì) JUnit 4 的向后兼容性。本文對(duì) JUnit 5 進(jìn)行了詳細(xì)的介紹。
JUnit 5 簡(jiǎn)介
與之前的版本不同,JUnit 5 由三個(gè)不同的模塊組成。第一個(gè)模塊是 JUnit 平臺(tái),其主要作用是在 JVM 上啟動(dòng)測(cè)試框架。它定義了一個(gè)抽象的 TestEngine API 來(lái)定義運(yùn)行在平臺(tái)上的測(cè)試框架,同時(shí)還支持通過(guò)命令行、Gradle 和 Maven 來(lái)運(yùn)行平臺(tái)。第二個(gè)模塊是 JUnit Jupiter,包含了 JUnit 5 新的編程模型和擴(kuò)展機(jī)制。第三個(gè)模塊是 JUnit Vintage,允許在平臺(tái)上運(yùn)行 JUnit 3 和 JUnit 4 的測(cè)試用例。
JUnit 5 對(duì) Java 運(yùn)行環(huán)境的低要求是 Java 8?梢栽 Eclipse 和 IntelliJ IDEA 上運(yùn)行 JUnit 5 測(cè)試。本文的示例基于 IntelliJ IDEA 上開(kāi)發(fā),并使用 Gradle 作為構(gòu)建工具。不過(guò)目前 IDE 對(duì) JUnit 5 的支持還比較有限,只有新版本的 IntelliJ IDEA 原生支持,在其它 IDE 上需要使用命令行工具來(lái)運(yùn)行。
編寫測(cè)試用例
JUnit 5 對(duì)編寫單元測(cè)試用例的方式做了一系列的改進(jìn),如下介紹。
JUnit 5 注解
JUnit 5 提供了一些常用的注解在編寫測(cè)試用例的時(shí)候使用。其中的一些注解和 JUnit 4 的注解有相同的名稱,不過(guò)所在的 Java 包變成了 org.junit.jupiter.api。常用的注解見(jiàn)表 1。
表 1. JUnit 5 常用注解
清單 1 中給出了使用這些注解編寫的單元測(cè)試用例。
清單 1. 使用常用注解的單元測(cè)試用例
@DisplayName("Calculator")
public class CalculatorTest {
private Calculator calculator;
@BeforeAll
public static void init() {
System.out.println("Start testing");
}
@BeforeEach
public void create() {
this.calculator = new Calculator();
}
@AfterEach
public void destroy() {
this.calculator = null;
}
@AfterAll
public static void cleanup() {
System.out.println("Finish testing");
}
@Test
@DisplayName("Test 1 + 2 = 3")
public void testAdd() {
assertEquals(3, this.calculator.add(1, 2));
}
@Test
@DisplayName("Test 3 - 2 = 1")
public void testSubtract() {
assertEquals(1, this.calculator.subtract(3, 2));
}
@Disabled
@Test
@DisplayName("disabled test")
public void ignoredTest() {
System.out.println("This test is disabled");
}
}
在這些注解中,實(shí)用的應(yīng)該是@DisplayName。通過(guò)@DisplayName,開(kāi)發(fā)人員可以為每個(gè)測(cè)試用例添加更具體的名字,更容易傳達(dá)用例所要測(cè)試的內(nèi)容。
通過(guò)@Tag 注解可以為測(cè)試類或方法添加標(biāo)簽,但是不同的標(biāo)簽只是通過(guò)字符串來(lái)進(jìn)行區(qū)分,并不是類型安全的。一個(gè)拼寫錯(cuò)誤可能造成標(biāo)簽沒(méi)有被正確應(yīng)用。更好的做法是使用類型安全的元注解(meta annotation)。編譯器會(huì)對(duì)元注解標(biāo)簽的正確性進(jìn)行驗(yàn)證,從而減少無(wú)意的錯(cuò)誤。清單 2 中定義了元注解標(biāo)簽@Remote,對(duì)應(yīng)于標(biāo)簽 remote。
清單 2. 元注解標(biāo)簽
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Tag("remote")
public @interface Remote {
}
清單 3 中展示了@Remote 的用法。使用@Remote 的作用等同于@Tag("Remote"),為 testGetUser 方法添加了標(biāo)簽 remote。使用@Remote 不僅提高了代碼的可讀性,也可以避免無(wú)意的拼寫錯(cuò)誤帶來(lái)的問(wèn)題。
清單 3. 使用元注解標(biāo)簽
@DisplayName("Remote test")
public class RemoteTest {
@Test
@Remote
public void testGetUser() {
System.out.println("Get user");
}
}
JUnit 5 斷言
斷言(assertions)是測(cè)試方法中的核心部分,用來(lái)對(duì)測(cè)試需要滿足的條件進(jìn)行驗(yàn)證。這些斷言方法都是 org.junit.jupiter.api.Assertions 的靜態(tài)方法。JUnit 5 內(nèi)置的斷言可以分成如下幾個(gè)類別:
第一類是簡(jiǎn)單斷言,用來(lái)對(duì)單個(gè)值進(jìn)行簡(jiǎn)單的驗(yàn)證,常用的方法見(jiàn)表 2。