更優(yōu)雅的單元測試——Spring Boot 集成 JUnit5

一、 為什么使用JUnit5

  • JUnit4被廣泛使用,但是許多場景下使用起來語法較為繁瑣,JUnit5中支持lambda表達式,語法簡單且代碼不冗余。
  • JUnit5易擴展,包容性強,可以接入其他的測試引擎。
  • 功能更強大提供了新的斷言機制、參數(shù)化測試、重復(fù)性測試等新功能。
  • ps:開發(fā)人員為什么還要測試,單測寫這么規(guī)范有必要嗎?其實單測是開發(fā)人員必備技能,只不過很多開發(fā)人員開發(fā)任務(wù)太重導(dǎo)致調(diào)試完就不管了,沒有系統(tǒng)化得單元測試,單元測試在CI/CD、編譯打包、回歸測試、系統(tǒng)重構(gòu)時能發(fā)揮巨大的作用,可以在重構(gòu)后快速測試新的接口是否與原設(shè)計有出入,改動是否破壞了原有邏輯?

二、 JUnit5簡介

JUnit5組成

如圖,JUnit5結(jié)構(gòu)如下:

  • JUnit Platform:這是Junit提供的平臺功能模塊,通過它,其它的測試引擎都可以接入Junit實現(xiàn)接口和執(zhí)行。
  • JUnit JUpiter:這是JUnit5的核心,是一個基于JUnit Platform的引擎實現(xiàn),它包含許多豐富的新特性來使得自動化測試更加方便和強大。
  • JUnit Vintage:這個模塊是兼容JUnit3、JUnit4版本的測試引擎,使得舊版本的自動化測試也可以在JUnit5下正常運行。

三、 依賴引入

我們以SpringBoot2.3.1為例,引入如下依賴,防止使用舊的junit4相關(guān)接口我們將其依賴排除。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    <exclusions>
        <exclusion>
            <groupId>org.junit.vintage</groupId>
            <artifactId>junit-vintage-engine</artifactId>
        </exclusion>
    </exclusions>
</dependency>

四、 常用注解

  • @BeforeEach:在每個單元測試方法執(zhí)行前都執(zhí)行一遍
  • @BeforeAll:在每個單元測試方法執(zhí)行前執(zhí)行一遍(只執(zhí)行一次)
  • @DisplayName("商品入庫測試"):用于指定單元測試的名稱
  • @Disabled:當前單元測試置為無效,即單元測試時跳過該測試
  • @RepeatedTest(n):重復(fù)性測試,即執(zhí)行n次
  • @ParameterizedTest:參數(shù)化測試,
  • @ValueSource(ints = {1, 2, 3}):參數(shù)化測試提供數(shù)據(jù)

五、 斷言

JUnit Jupiter提供了強大的斷言方法用以驗證結(jié)果,在使用時需要借助java8的新特性lambda表達式,均是來自org.junit.jupiter.api.Assertions包的static方法。

assertTrueassertFalse用來判斷條件是否為truefalse


@Test
@DisplayName("測試斷言equals")
void testEquals() {
    assertTrue(3 < 4);
}

assertNullassertNotNull用來判斷條件是否為null`

@Test
@DisplayName("測試斷言NotNull")
void testNotNull() {
    assertNotNull(new Object());
}

assertThrows用來判斷執(zhí)行拋出的異常是否符合預(yù)期,并可以使用異常類型接收返回值進行其他操作

@Test
@DisplayName("測試斷言拋異常")
void testThrows() {
    ArithmeticException arithExcep = assertThrows(ArithmeticException.class, () -> {
        int m = 5/0;
    });
    assertEquals("/ by zero", arithExcep.getMessage());
}

assertTimeout用來判斷執(zhí)行過程是否超時

@Test
@DisplayName("測試斷言超時")
void testTimeOut() {
    String actualResult = assertTimeout(ofSeconds(2), () -> {
        Thread.sleep(1000);
        return "a result";
    });
    System.out.println(actualResult);
}

assertAll是組合斷言,當它內(nèi)部所有斷言正確執(zhí)行完才算通過

@Test
@DisplayName("測試組合斷言")
void testAll() {
    assertAll("測試item商品下單",
            () -> {
                //模擬用戶余額扣減
                assertTrue(1 < 2, "余額不足");
            },
            () -> {
                //模擬item數(shù)據(jù)庫扣減庫存
                assertTrue(3 < 4);
            },
            () -> {
                //模擬交易流水落庫
                assertNotNull(new Object());
            }
    );
}

五、 重復(fù)性測試

在許多場景中我們需要對同一個接口方法進行重復(fù)測試,例如對冪等性接口的測試。

JUnit Jupiter通過使用@RepeatedTest(n)指定需要重復(fù)的次數(shù)

@RepeatedTest(3)
@DisplayName("重復(fù)測試")
void repeatedTest() {
    System.out.println("調(diào)用");
}
重復(fù)性測試

六、 參數(shù)化測試

參數(shù)化測試可以按照多個參數(shù)分別運行多次單元測試這里有點類似于重復(fù)性測試,只不過每次運行傳入的參數(shù)不用。需要使用到@ParameterizedTest,同時也需要@ValueSource提供一組數(shù)據(jù),它支持八種基本類型以及String和自定義對象類型,使用極其方便。

@ParameterizedTest
@ValueSource(ints = {1, 2, 3})
@DisplayName("參數(shù)化測試")
void paramTest(int a) {
    assertTrue(a > 0 && a < 4);
}

七、 內(nèi)嵌測試

JUnit5提供了嵌套單元測試的功能,可以更好展示測試類之間的業(yè)務(wù)邏輯關(guān)系,我們通常是一個業(yè)務(wù)對應(yīng)一個測試類,有業(yè)務(wù)關(guān)系的類其實可以寫在一起。這樣有利于進行測試。而且內(nèi)聯(lián)的寫法可以大大減少不必要的類,精簡項目,防止類爆炸等一系列問題。

@SpringBootTest
@AutoConfigureMockMvc
@DisplayName("Junit5單元測試")
public class MockTest {
    //....
    @Nested
    @DisplayName("內(nèi)嵌訂單測試")
    class OrderTestClas {
        @Test
        @DisplayName("取消訂單")
        void cancelOrder() {
            int status = -1;
            System.out.println("取消訂單成功,訂單狀態(tài)為:"+status);
        }
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容