這裡寫目錄标題
- 單元測試-JUnit5簡介
- 單元測試-常用測試注解
- 單元測試-斷言機制
-
- 簡單斷言
- 數組斷言
- 組合斷言
- 異常斷言
- 逾時斷言
- 快速失敗
- 單元測試-前置條件
- 單元測試-參數化測試
單元測試-JUnit5簡介
Spring Boot 2.2.0 版本開始引入 JUnit 5 作為單元測試預設庫
JUnit 5官方文檔
作為最新版本的JUnit架構,JUnit5與之前版本的JUnit架構有很大的不同。由三個不同子項目的幾個不同子產品組成。
JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage
JUnit Platform: Junit Platform是在JVM上啟動測試架構的基礎,不僅支援
Junit自制的測試引擎,其他測試引擎也都可以接入。
JUnit Jupiter: JUnit Jupiter提供了JUnit5的新的程式設計模型,是JUnit5新
特性的核心。内部包含了一個測試引擎,用于在Junit Platform上運作。
JUnit Vintage: 由于JUint已經發展多年,為了照顧老的項目,
JUnit Vintage提供了相容JUnit4.x,JUnit3.x的測試引擎。
注意:
- SpringBoot 2.4 以上版本移除了預設對 Vintage 的依賴。如果需要相容JUnit4需要自行引入(不能使用JUnit4的功能 @Test)
- JUnit 5’s Vintage已經從spring-boot-starter-test從移除。如果需要繼續相容Junit4需要自行引入Vintage依賴:
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
</exclusion>
</exclusions>
</dependency>
使用添加JUnit 5,添加對應的starter:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
Spring的JUnit 5的基本單元測試模闆(Spring的JUnit4的是@[email protected](SpringRunner.class)):
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;//注意不是org.junit.Test(這是JUnit4版本的)
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class SpringBootApplicationTests {
@Autowired
private Component component;
@Test
//@Transactional 标注後連接配接資料庫有復原功能
public void contextLoads() {
Assertions.assertEquals(5, component.getFive());
}
}
單元測試-常用測試注解
官方文檔 - Annotations
@Test:表示方法是測試方法。但是與JUnit4的@Test不同,他的職責非常單一不
能聲明任何屬性,拓展的測試将會由Jupiter提供額外測試
@ParameterizedTest:表示方法是參數化測試。
@RepeatedTest:表示方法可重複執行。
@DisplayName:為測試類或者測試方法設定展示名稱。
@BeforeEach:表示在每個單元測試之前執行。
@AfterEach:表示在每個單元測試之後執行。
@BeforeAll:表示在所有單元測試之前執行。
@AfterAll:表示在所有單元測試之後執行。
@Tag:表示單元測試類别,類似于JUnit4中的@Categories。
@Disabled:表示測試類或測試方法不執行,類似于JUnit4中的@Ignore。
@Timeout:表示測試方法運作如果超過了指定時間将會傳回錯誤。
@ExtendWith:為測試類或測試方法提供擴充類引用。
import org.junit.jupiter.api.*;
@DisplayName("junit5功能測試類")
public class Junit5Test {
@DisplayName("測試displayname注解")
@Test
void testDisplayName() {
System.out.println(1);
System.out.println(jdbcTemplate);
}
@ParameterizedTest
@ValueSource(strings = { "racecar", "radar", "able was I ere I saw elba" })
void palindromes(String candidate) {
assertTrue(StringUtils.isPalindrome(candidate));
}
@Disabled
@DisplayName("測試方法2")
@Test
void test2() {
System.out.println(2);
}
@RepeatedTest(5)
@Test
void test3() {
System.out.println(5);
}
/**
* 規定方法逾時時間。超出時間測試出異常
*
* @throws InterruptedException
*/
@Timeout(value = 500, unit = TimeUnit.MILLISECONDS)
@Test
void testTimeout() throws InterruptedException {
Thread.sleep(600);
}
@BeforeEach
void testBeforeEach() {
System.out.println("測試就要開始了...");
}
@AfterEach
void testAfterEach() {
System.out.println("測試結束了...");
}
@BeforeAll
static void testBeforeAll() {
System.out.println("所有測試就要開始了...");
}
@AfterAll
static void testAfterAll() {
System.out.println("所有測試以及結束了...");
}
}
單元測試-斷言機制
斷言Assertion是測試方法中的核心部分,用來對測試需要滿足的條件進行驗證。這些斷言方法都是org.junit.jupiter.api.Assertions的靜态方法。檢查業務邏輯傳回的資料是否合理。所有的測試運作結束以後,會有一個詳細的測試報告。
JUnit 5 内置的斷言可以分成如下幾個類别:
簡單斷言
用來對單個值進行簡單的驗證。如:
@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());
}
數組斷言
通過 assertArrayEquals 方法來判斷兩個對象或原始類型的數組是否相等。
@Test
@DisplayName("array assertion")
public void array() {
assertArrayEquals(new int[]{1, 2}, new int[] {1, 2});
}
組合斷言
assertAll()方法接受多個 org.junit.jupiter.api.Executable 函數式接口的執行個體作為要驗證的斷言,可以通過 lambda 表達式很容易的提供這些斷言。
@Test
@DisplayName("assert all")
public void all() {
assertAll("Math",
() -> assertEquals(2, 1 + 1),
() -> assertTrue(1 > 0)
);
}
異常斷言
在JUnit4時期,想要測試方法的異常情況時,需要用@Rule注解的ExpectedException變量還是比較麻煩的。而JUnit5提供了一種新的斷言方式Assertions.assertThrows(),配合函數式程式設計就可以進行使用。
@Test
@DisplayName("異常測試")
public void exceptionTest() {
ArithmeticException exception = Assertions.assertThrows(
//扔出斷言異常
ArithmeticException.class, () -> System.out.println(1 % 0));
}
逾時斷言
JUnit5還提供了Assertions.assertTimeout()為測試方法設定了逾時時間。
@Test
@DisplayName("逾時測試")
public void timeoutTest() {
//如果測試方法時間超過1s将會異常
Assertions.assertTimeout(Duration.ofMillis(1000), () -> Thread.sleep(500));
}
快速失敗
通過 fail 方法直接使得測試失敗。
@Test
@DisplayName("fail")
public void shouldFail() {
fail("This should fail");
}
單元測試-前置條件
Unit 5 中的前置條件(assumptions【假設】)類似于斷言,不同之處在于不滿足的斷言assertions會使得測試方法失敗,而不滿足的前置條件隻會使得測試方法的執行終止。
前置條件可以看成是測試方法執行的前提,當該前提不滿足時,就沒有繼續執行的必要。
@DisplayName("前置條件")
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")
);
}
}
assumeTrue 和 assumFalse 確定給定的條件為 true 或 false,不滿足條件會使得測試執行終止。
assumingThat 的參數是表示條件的布爾值和對應的 Executable 接口的實作對象。隻有條件滿足時,Executable 對象才會被執行;當條件不滿足時,測試執行并不會終止。
單元測試-參數化測試
官方文檔 - Parameterized Tests
- 參數化測試是JUnit5很重要的一個新特性,它使得用不同的參數多次運作測試成為了可能,也為我們的單元測試帶來許多便利。
- 利用@ValueSource等注解,指定入參,我們将可以使用不同的參數進行多次單元測試,而不需要每新增一個參數就新增一個單元測試,省去了很多備援代碼。
- 利用**@ValueSource**等注解,指定入參,我們将可以使用不同的參數進行多次單元測試,而不需要每新增一個參數就新增一個單元測試,省去了很多備援代碼。
@ValueSource: 為參數化測試指定入參來源,支援八大基礎類以及String類
型,Class類型
@NullSource: 表示為參數化測試提供一個null的入參
@EnumSource: 表示為參數化測試提供一個枚舉入參
@CsvFileSource:表示讀取指定CSV檔案内容作為參數化測試入參
@MethodSource:表示讀取指定方法的傳回值作為參數化測試入參
(注意方法傳回需要是一個流)
當然如果參數化測試僅僅隻能做到指定普通的入參還達不到讓我覺得驚豔的地步。讓我真正感到他的強大之處的地方在于他可以支援外部的各類入參。如:CSV,YML,JSON 檔案甚至方法的傳回值也可以作為入參。隻需要去實作ArgumentsProvider接口,任何外部檔案都可以作為它的入參。
@ParameterizedTest
@ValueSource(strings = {"one", "two", "three"})
@DisplayName("參數化測試1")
public void parameterizedTest1(String string) {
System.out.println(string);
Assertions.assertTrue(StringUtils.isNotBlank(string));
}
@ParameterizedTest
@MethodSource("method") //指定方法名
@DisplayName("方法來源參數")
public void testWithExplicitLocalMethodSource(String name) {
System.out.println(name);
Assertions.assertNotNull(name);
}
static Stream<String> method() {
return Stream.of("apple", "banana");
}
遷移指南
官方文檔 - Migrating from JUnit 4
在進行遷移的時候需要注意如下的變化:
注解在 org.junit.jupiter.api 包中,斷言在
org.junit.jupiter.api.Assertions 類中,前置條件在
org.junit.jupiter.api.Assumptions 類中。
把@Before 和@After 替換成@BeforeEach 和@AfterEach。
把@BeforeClass 和@AfterClass 替換成@BeforeAll 和@AfterAll。
把@Ignore 替換成@Disabled。
把@Category 替換成@Tag。
把@RunWith、@Rule 和@ClassRule 替換成@ExtendWith。