天天看點

JUnit:求求你了,别再用 main 方法測試了,好嗎?(2)

03、瞻前顧後

在一個測試用例中,可能要對多個方法進行測試。在測試之前呢,需要準備一些條件,比如說建立對象;在測試完成後呢,需要把這些對象銷毀掉以釋放資源。如果在多個測試方法中重複這些樣闆代碼又會顯得非常啰嗦。

這時候,該怎麼辦呢?

我為你提供了 setUp() 和 tearDown(),作為一個文化人,我稱之為“瞻前顧後”。來看要測試的代碼。

public class Calculator {
    public int sub(int a, int b) {
        return a - b;
    }
    public int add(int a, int b) {
        return a + b;
    }
}      

建立測試用例的時候記得勾選setUp 和 tearDown。

生成後的代碼如下所示。

class CalculatorTest {
    Calculator calculator;
    @BeforeEach
    void setUp() {
        calculator = new Calculator();
    }
    @AfterEach
    void tearDown() {
        calculator = null;
    }
    @Test
    void sub() {
        assertEquals(0,calculator.sub(1,1));
    }
    @Test
    void add() {
        assertEquals(2,calculator.add(1,1));
    }
}      

@BeforeEach 的 setUp() 方法會在運作每個 @Test 方法之前運作;@AfterEach 的 tearDown() 方法會在運作每個 @Test 方法之後運作。

與之對應的還有 @BeforeAll 和 @AfterAll,與 @BeforeEach 和 @AfterEach 不同的是,All 通常用來初始化和銷毀靜态變量。

public class DatabaseTest {

   static Database db;

   @BeforeAll

   public static void init() {

       db = createDb(...);

   }

   @AfterAll

   public static void drop() {

       ...

}

03、異常測試

對于 Java 程式來說,異常處理也非常的重要。對于可能抛出的異常進行測試,本身也是測試的一個重要環節。

還拿之前的 Factorial 類來進行說明。在 fact() 方法的一開始,對參數 n 進行了校驗,如果小于 0,則抛出 IllegalArgumentException 異常。

public class Factorial {

   public static long fact(long n) {

       if (n < 0) {

           throw new IllegalArgumentException("參數不能小于 0");

       }

       long r = 1;

       for (long i = 1; i <= n; i++) {

           r = r * i;

       return r;

在 FactorialTest 中追加一個測試方法 factIllegalArgument()。

@Test

void factIllegalArgument() {

   assertThrows(IllegalArgumentException.class, new Executable() {

       @Override

       public void execute() throws Throwable {

           Factorial.fact(-2);

   });

我為你提供了一個 assertThrows() 的方法,第一個參數是異常的類型,第二個參數 Executable,可以封裝産生異常的代碼。如果覺得匿名内部類寫起來比較複雜的話,可以使用 Lambda 表達式。

void factIllegalArgumentLambda() {

   assertThrows(IllegalArgumentException.class, () -> {

       Factorial.fact(-2);

04、忽略測試

有時候,由于某些原因,某些方法産生了 bug,需要一段時間去修複,在修複之前,該方法對應的測試用例一直是以失敗告終的,為了避免這種情況,我為你提供了 @Disabled 注解。

class DisabledTestsDemo {
    @Disabled("該測試用例不再執行,直到編号為 43 的 bug 修複掉")
    @Test
    void testWillBeSkipped() {
    }
    @Test
    void testWillBeExecuted() {
    }
}      

@Disabled 注解也可以不需要說明,但我建議你還是提供一下,簡單地說明一下為什麼這個測試方法要忽略。在上例中,如果團隊的其他成員看到說明就會明白,當編号 43 的 bug 修複後,該測試方法會重新啟用的。即便是為了提醒自己,也很有必要,因為時間長了你可能自己就忘了,當初是為什麼要忽略這個測試方法的。

05、條件測試

有時候,你可能需要在某些條件下運作測試方法,有些條件下不運作測試方法。針對這場使用場景,我為你提供了條件測試。

1)不同的作業系統,可能需要不同的測試用例,比如說 Linux 和 Windows 的路徑名是不一樣的,通過 @EnabledOnOs 注解就可以針對不同的作業系統啟用不同的測試用例。

@Test
@EnabledOnOs(MAC)
void onlyOnMacOs() {
    // ...
}
@TestOnMac
void testOnMac() {
    // ...
}
@Test
@EnabledOnOs({ LINUX, MAC })
void onLinuxOrMac() {
    // ...
}
@Test
@DisabledOnOs(WINDOWS)
void notOnWindows() {
    // ...
}      

2)不同的 Java 運作環境,可能也需要不同的測試用例。@EnabledOnJre 和 @EnabledForJreRange 注解就可以滿足這個需求。

@Test
@EnabledOnJre(JAVA_8)
void onlyOnJava8() {
    // ...
}
@Test
@EnabledOnJre({ JAVA_9, JAVA_10 })
void onJava9Or10() {
    // ...
}
@Test
@EnabledForJreRange(min = JAVA_9, max = JAVA_11)
void fromJava9to11() {
    // ...
}      

06、尾聲

最後,給你說三句心裡話吧。在編寫單元測試的時候,你最好這樣做:

1)單元測試的代碼本身必須非常名單明了,能一下看明白,決不能再為測試代碼編寫測試代碼。

2)每個單元測試應該互相獨立,不依賴運作時的順序。

3)測試時要特别注意邊界條件,比如說 0,null,空字元串"" 等情況。

希望我能盡早的替你發現代碼中的 bug,畢竟越早的發現,造成的損失就會越小。see you!

推薦閱讀:

鵝廠大佬掏心掏肺的分享:我的程式設計能力從什麼時候開始突飛猛進?

Github 瘋傳!史上最強!BAT 大佬「LeetCode刷題手冊」電子書開放下載下傳了!

如果覺得有幫助的話,請不要吝啬一鍵三連哦,我知道,你們都是有素質的 CSDN 好公民,2021 年,讓我們一起加油!