本文假定讀者有單元測試基礎,不會對單元測試的概念做過多的介紹,主要講解junit5的新功能用法,讓讀者快速上手Junit5。如果你想了解單元測試的基礎概念可以閱讀文章:一文搞定單元測試核心概念
注意:建議大家使用文章中推薦的jdk、Eclipse、mvn以及 pom.xml進行配置,可以確保大家代碼的順利運行!
JUnit5 框架構成
Junit5需要Java 8或更高版本,和 Junit4 只是一個單獨的 Jar 包不同,目前的 Junit5 組成如下:
JUnit 5= JUnit Platform + JUnit Jupiter + JUnit Vintage
JUnit Platform是 Junit 向測試平臺演進,提供平臺功能的模塊,通過 JUnit Platform,其他的自動化測試引擎或開發(fā)人員自己定制的引擎都可以接入Junit 實現(xiàn)對接和執(zhí)行
JUnit Jupiter, 這是 Junit5 的核心,可以看作是承載 Junit4 原有功能的演進,它包含了很多豐富的新特性來使 JUnit 自動化測試更加方便、功能更加豐富和強大。本系列就會重點圍繞 Jupiter 中的一些特性進行介紹。Jupiter 本身也是一個基于 Junit Platform 的引擎實現(xiàn)。
JUnit Vintage,Junit發(fā)展了10數(shù)年,Junit 3 和 Junit 4 都積累了大量的用戶,作為新一代框架,這個模塊是對 JUnit3,JUnit4 版本兼容的測試引擎,使舊版本 junit 的自動化測試腳本也可以順暢運行在 Junit5 下,它也可以看作是基于Junit Platform 實現(xiàn)的引擎范例。
Eclipse環(huán)境搭建
Eclipse從Oxygen.1a(4.7.1a) 開始支持Junit5
在Eclipse的 Marketplace中搜索Junit5,然后安全JUnit-Tools 如下圖所示:

安裝插件后,會在eclipse中自動裝入junit5 Library,如下圖:

Maven,強烈推薦如下配置!
<properties>
????????????? <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
????????????? <maven.compiler.source>1.8</maven.compiler.source>
????????????? <maven.compiler.target>${maven.compiler.source}</maven.compiler.target>
????????????? <junit.jupiter.version>5.5.2</junit.jupiter.version>
????????????? <junit.platform.version>1.5.2</junit.platform.version>
?????? </properties>
?????? <dependencies>
????????????? <dependency>
?????? ????????????? <groupId>org.junit.jupiter</groupId>
???????????????????? <artifactId>junit-jupiter-engine</artifactId>
???????????????????? <version>${junit.jupiter.version}</version>
???????????????????? <scope>test</scope>
????????????? </dependency>
????????????? <dependency>
???????????????????? <groupId>org.junit.platform</groupId>
???????????????????? <artifactId>junit-platform-runner</artifactId>
???????????????????? <version>${junit.platform.version}</version>
???????????????????? <scope>test</scope>
????????????? </dependency>
?????? </dependencies>
?????? <build>
????????????? <plugins>
???????????????????? <plugin>
??????????????????????????? <artifactId>maven-compiler-plugin</artifactId>
??????????????????????????? <version>3.8.1</version>
???????????????????? </plugin>
????????????? ?????? <plugin>
??????????????????????????? <artifactId>maven-surefire-plugin</artifactId>
??????????????????????????? <version>2.22.2</version>
???????????????????? </plugin>
????????????? </plugins>
?????? </build>
特色功能
新增的斷言
assertAll:用來校驗所有斷言是否為真。如下代碼會導致校驗失敗
assertAll("person",
?????? ??????????????? () ->assertEquals("John","John"),
?????? ??????????????? () ->assertEquals("Doe","kevin")
?????? ???????);
assertTimeout:斷言在超出給定超時之前,所提供的可執(zhí)行代碼塊的執(zhí)行完成。
assertTimeout(ofSeconds(1),() -> {
?????? ????????????? // Simulate task that takes morethan 10ms.
?????? ????????????? Thread.sleep(2000);
?????? ?????????});
四個變化的注解
@BeforeAll 只執(zhí)行一次,執(zhí)行時機是在所有測試和@BeforeEach 注解方法之前。
@BeforeEach 在每個測試執(zhí)行之前執(zhí)行。
@AfterEach 在每個測試執(zhí)行之后執(zhí)行。
@AfterAll 只執(zhí)行一次,執(zhí)行時機是在所有測試和 @AfterEach 注解方法之后。
新增的實用標簽,在指定條件下運行用例
新增Enabled相關標簽,表示在指定系統(tǒng)、指定jdk版本等等條件下運行。詳情如下:
用例只在指定系統(tǒng)中運行
@EnabledOnOs({ LINUX, MAC })
用例只在指定JDK版本時運行
@EnabledOnJre(JAVA_8)
用例只能在64位操作系統(tǒng)上執(zhí)行的測試
@EnabledIfSystemProperty(named = "os.arch", matches = ".*64.*")
需要傳入環(huán)境變量DEBUG=true才能執(zhí)行的測試@EnabledIfEnvironmentVariable(named = "DEBUG", matches = "true")
用例顯示自定義名稱
@DisplayName("測試用例1")
例如設置
@Test
?????? ? @DisplayName("測試用例1")
?????? ??? void succeedingTest() {
????????????? ? assertAll("person",
?????? ??????????????? () ->assertEquals("John","John"),
?????? ??????????????? () ->assertEquals("Doe","kevin")
?????? ??????? );
?????????????? ??? }
在執(zhí)行結果中顯示

用例無效
@Disabled
重復執(zhí)行用例
@RepeatedTest(10)
用例超時
@Timeout(5)
嵌套
@Nested設計目的就是在測試類中嵌套其他測試類用以表明用例組之間的關系,個人感覺應用場景有限。
例如:
class NestedDemo {
??? @Test
??? void case1() {
????? assertTrue(true);
??? }
??? @Nested
??? class NewTest {
??????? @Test
??????? void caseN1() {
??????? ? ?assertTrue(true);
??????? ? ?}
??????? @Test???
??????? void caseN2() {
?????? ??? ?assertTrue(false);
??????? }
??????? @Test
??????? void caseN3() {
?????? ??? ?assertTrue(true);}
??? }
}
運行時會讓你選擇執(zhí)行哪個測試類

參數(shù)化
@ParameterizedTest
@ValueSource(strings = { "racecar", "radar", "able was I ere I saw elba"})
void palindromes(Stringcandidate) {
??? assertTrue(StringUtils.isPalindrome(candidate));
}
@ParameterizedTest
@CsvSource({
??? "apple,???????? 1",
??? "banana,??????? 2",
??? "'lemon, lime', 0xF1"
})
void testWithCsvSource(String fruit, intrank) {
??? assertNotNull(fruit);
??? assertNotEquals(0, rank);
}
@ValueSource:聲明一個基本類型的數(shù)組(String、int、long、double等),并且為測試方法提供調用參數(shù),每個參數(shù)執(zhí)行一次測試方法。
@EnumSource:為測試方法提供Enum常量參數(shù),這個注釋提供一個可選的name參數(shù),通過其指定使用那些常量,如:names = {"DAY", "HOURS"}。還有一個可選的mode參數(shù),能夠通過names中聲明的常量列表或者正則表達式來細粒度地控制那些常量將會被傳遞到測試方法中。
@MethodSource:可是通過這個注釋引用測試類中的一個或者多個工廠方法,這些方法必須返回一個Stream、Interable、Iterator或者數(shù)組參數(shù),工廠方法不能接收任何參數(shù)。默認情況下必須是static方法,除非聲明了@TestInstace(Lifecycle.PER_CLASS)。如果測試方法中有多個參數(shù),則需要返回一個Arguments實例的集合。
@CsvSource:在參數(shù)化測試方法中,通過參數(shù)列表聲明參數(shù),每組參數(shù)的不同值用逗號隔開。
@CsvFileSource:使用類路徑中的CSV文件作為參數(shù),每一行數(shù)據(jù)都會觸發(fā)參數(shù)化測試的一次調用。
@ArgumentsSource:指定一個實現(xiàn)ArgumentsProvider接口的參數(shù)提供類作為參數(shù)化測試方法的參數(shù)提供者,這個類的provideArguments方法返回的Stream作為參數(shù)流。
用例執(zhí)行順序
import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
@TestMethodOrder(OrderAnnotation.class)
class OrderedTestsDemo{
??? @Test
??? @Order(1)
??? voidnullValues() {
??????? // perform assertions against null values
??? }
??? @Test
??? @Order(2)
??? voidemptyValues() {
??????? // perform assertions against empty values
??? }
??? @Test
??? @Order(3)
??? voidvalidValues() {
??????? // perform assertions against valid values
??? }
}
自定義標簽的使用
標簽生成
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Tag("smoke")
@Test
public @interface Smoke{}
用例中使用標簽
import staticorg.junit.Assert.assertTrue;
import org.junit.Test;
import com.my.demo.Smoke;
public class TestTagDemo {
?????? @Test
?????? @Smoke
?????? public void case1() {
????????????? System.out.println("case1");
?????? ??? assertTrue(true);
?????? }
?????? @Test
?????? @Smoke
?????? public void case2() {
????????????? System.out.println("case2");
?????? ??? assertTrue(false);
?????? }
?????? @Test
?????? public void case3() {
????????????? System.out.println("case3");
?????? ??? assertTrue(false);
?????? }
}
運行帶標簽的用例
Run As>Run Configuration

運行結果

如上圖所示,只運行了@Somke標簽的用例
maven-surefire-plugin中運行
在pom.xml文件中配置
<plugins>
? <plugin>
??? <artifactId>maven-surefire-plugin</artifactId>
??? <version>3.0.0-M4</version>
??????????????? <configuration>
??????????????? <groups>smoke</groups>
??????????? </configuration>
</plugin>
注意,想要通過maven構建的時候執(zhí)行用例,用例命名必須遵循如下格式:
?**/Test*.java
?**/*Test.java
?**/*Tests.java
?**/*TestCase.java
Suite
在JUnit 5 中使用@RunWith,@SelectPackages和@SelectClasses作為suite的常用方法。還可以配合@IncludeTags,執(zhí)行需要具有某標簽的用例。
代碼如下所示:
@RunWith(JUnitPlatform.class)
@SelectClasses( ClassATest.class ) //執(zhí)行某個類
@SelectPackages({"com.examples.packageA","com.examples.packageB"})
//執(zhí)行某個包中的所有用例
public class JUnit5TestSuiteExample
{
}
注意:我在實際操作中發(fā)現(xiàn)在IDE中使用junit5,對其版本信息要求極高,強烈建議大家
使用如下配置(本機測試通過)
推薦jdk:1.8.0_151(1.8即可)
推薦Eclipse:Photon Release (4.8.0)
推薦mvn版本:3.6.3
推薦 pom配置
<properties>
????????????? <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
????????????? <maven.compiler.source>1.8</maven.compiler.source>
????????????? <maven.compiler.target>${maven.compiler.source}</maven.compiler.target>
????????????? <junit.jupiter.version>5.5.2</junit.jupiter.version>
????????????? <junit.platform.version>1.5.2</junit.platform.version>
?????? </properties>
?????? <dependencies>
????????????? <dependency>
???????????????????? <groupId>org.junit.jupiter</groupId>
???????????????????? <artifactId>junit-jupiter-engine</artifactId>
???????????????????? <version>${junit.jupiter.version}</version>
???????????????????? <scope>test</scope>
????????????? </dependency>
????????????? <dependency>
???????????????????? <groupId>org.junit.platform</groupId>
???????????????????? <artifactId>junit-platform-runner</artifactId>
???????????????????? <version>${junit.platform.version}</version>
???????????????????? <scope>test</scope>
????????????? </dependency>
?????? </dependencies>
?????? <build>
????????????? <plugins>
???????????????????? <plugin>
??????????????????????????? <artifactId>maven-compiler-plugin</artifactId>
??????????????????????????? <version>3.8.1</version>
???????????????????? </plugin>
???????????????????? <plugin>
??????????????????????????? <artifactId>maven-surefire-plugin</artifactId>
??????????????????????????? <version>2.22.2</version>
???????????????????? </plugin>
????????????? </plugins>
?????? </build>
</project>
動態(tài)測試
注解@Test可以認為是靜態(tài)測試方法,是在編譯時指定的。在JUnit5中還有一類測試稱為動態(tài)測試,可以在運行時改變。
@TestFactory:聲明動態(tài)測試,聲明的方法本身不是測試用例,而是生成測試用例的工廠方法。這個方法必須返回一個DynamicNode實例的Stream、Collection、Iterable或Iterator。DynamicNode有兩個可實例化子類DynamicContainer和DynamicTest。動態(tài)測試的執(zhí)行生命周期和@Test測試不同,同一個@TestFactory方法所生成的n個動態(tài)測試,@BeforeEach和@AfterEach只會在n個動態(tài)測試開始前和結束后執(zhí)行一次,不會為每個單獨的動態(tài)測試都執(zhí)行。個人感覺應用場景有限,這里就不做詳細介紹了。
總結
大家可以看到Junit5跟Junit4比有了很大的變化,個人認為其主要目的是對標TestNG,可以說Junit5跟TestNG更像了!本文內容很多,建議大家只需要有個大概的印象即可,在實際工作中如果使用了Junit5在或過頭來仔細閱讀并結合實際應用!原創(chuàng)不易,如果文章幫助了大家,歡迎轉發(fā)!