自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

JUnit:別再用 main 方法測(cè)試了,好嗎?

開發(fā) 后端
在了解我之前,先來了解一下什么是單元測(cè)試。單元測(cè)試,就是針對(duì)最小的功能單元編寫測(cè)試代碼。在 Java 中,最小的功能單元就是方法,因此,對(duì) Java 程序員進(jìn)行單元測(cè)試實(shí)際上就是對(duì) Java 方法的測(cè)試。

[[361023]]

01、前世今生

你好呀,我是 JUnit,一個(gè)開源的 Java 單元測(cè)試框架。在了解我之前,先來了解一下什么是單元測(cè)試。單元測(cè)試,就是針對(duì)最小的功能單元編寫測(cè)試代碼。在 Java 中,最小的功能單元就是方法,因此,對(duì) Java 程序員進(jìn)行單元測(cè)試實(shí)際上就是對(duì) Java 方法的測(cè)試。

為什么要進(jìn)行單元測(cè)試呢?因?yàn)閱卧獪y(cè)試可以確保你編寫的代碼是符合軟件需求和遵循開發(fā)規(guī)范的。單元測(cè)試是所有測(cè)試中最底層的一類測(cè)試,是第一個(gè)環(huán)節(jié),也是最重要的一個(gè)環(huán)節(jié),是唯一一次能夠達(dá)到代碼覆蓋率 100% 的測(cè)試,是整個(gè)軟件測(cè)試過程的基礎(chǔ)和前提??梢赃@么說,單元測(cè)試的性價(jià)比是最好的。

微軟公司之前有這樣一個(gè)統(tǒng)計(jì):bug 在單元測(cè)試階段被發(fā)現(xiàn)的平均耗時(shí)是 3.25 小時(shí),如果遺漏到系統(tǒng)測(cè)試則需要 11.5 個(gè)小時(shí)。

 

經(jīng)我這么一說,你應(yīng)該已經(jīng)很清楚單元測(cè)試的重要性了。那在你最初編寫測(cè)試代碼的時(shí)候,是不是經(jīng)常這么做?就像下面這樣。

  1. public class Factorial { 
  2.     public static long fact(long n) { 
  3.         long r = 1; 
  4.         for (long i = 1; i <= n; i++) { 
  5.             r = r * i; 
  6.         } 
  7.         return r; 
  8.     } 
  9.  
  10.     public static void main(String[] args) { 
  11.         if (fact(3) == 6) { 
  12.             System.out.println("通過"); 
  13.         } else { 
  14.             System.out.println("失敗"); 
  15.         } 
  16.     } 

要測(cè)試 fact() 方法正確性,你在 main() 方法中編寫了一段測(cè)試代碼。如果你這么做過的話,我只能說你也曾經(jīng)青澀天真過啊!使用 main() 方法來測(cè)試有很多壞處,比如說:

1)測(cè)試代碼沒有和源代碼分開。

2)不夠靈活,很難編寫一組通用的測(cè)試代碼。

3)無法自動(dòng)打印出預(yù)期和實(shí)際的結(jié)果,沒辦法比對(duì)。

但如果學(xué)會(huì)使用我——JUnit 的話,就不會(huì)再有這種困擾了。我可以非常簡(jiǎn)單地組織測(cè)試代碼,并隨時(shí)運(yùn)行它們,還能給出準(zhǔn)確的測(cè)試報(bào)告,讓你在最短的時(shí)間內(nèi)發(fā)現(xiàn)自己編寫的代碼到底哪里出了問題。

02、上手指南

好了,既然知道了我這么優(yōu)秀,那還等什么,直接上手吧!我最新的版本是 JUnit 5,Intellij IDEA 中已經(jīng)集成了,所以你可以直接在 IDEA 中編寫并運(yùn)行我的測(cè)試用例。

第一步,直接在當(dāng)前的代碼編輯器窗口中按下 Command+N 鍵(Mac 版),在彈出的菜單中選擇「Test...」。

 

勾選上要編寫測(cè)試用例的方法 fact(),然后點(diǎn)擊「OK」。

此時(shí),IDEA 會(huì)自動(dòng)在當(dāng)前類所在的包下生成一個(gè)類名帶 Test(慣例)的測(cè)試類。如下圖所示。

 

如果你是第一次使用我的話,IDEA 會(huì)提示你導(dǎo)入我的依賴包。建議你選擇最新的 JUnit 5.4。

 

導(dǎo)入完畢后,你可以打開 pom.xml 文件確認(rèn)一下,里面多了對(duì)我的依賴。

  1. <dependency> 
  2.     <groupId>org.junit.jupiter</groupId> 
  3.     <artifactId>junit-jupiter</artifactId> 
  4.     <version>RELEASE</version> 
  5.     <scope>compile</scope> 
  6. </dependency> 

第二步,在測(cè)試方法中添加一組斷言,如下所示。

  1. @Test 
  2. void fact() { 
  3.     assertEquals(1, Factorial.fact(1)); 
  4.     assertEquals(2, Factorial.fact(2)); 
  5.     assertEquals(6, Factorial.fact(3)); 
  6.     assertEquals(100, Factorial.fact(5)); 

@Test 注解是我要求的,我會(huì)把帶有 @Test 的方法識(shí)別為測(cè)試方法。在測(cè)試方法內(nèi)部,你可以使用 assertEquals() 對(duì)期望的值和實(shí)際的值進(jìn)行比對(duì)。

第三步,你可以在郵件菜單中選擇「Run FactorialTest」來運(yùn)行測(cè)試用例,結(jié)果如下所示。

 

測(cè)試失敗了,因?yàn)榈?20 行的預(yù)期結(jié)果和實(shí)際不符,預(yù)期是 100,實(shí)際是 120。此時(shí),你要么修正實(shí)現(xiàn)代碼,要么修正測(cè)試代碼,直到測(cè)試通過為止。

 

不難吧?單元測(cè)試可以確保單個(gè)方法按照正確的預(yù)期運(yùn)行,如果你修改了某個(gè)方法的代碼,只需確保其對(duì)應(yīng)的單元測(cè)試通過,即可認(rèn)為改動(dòng)是沒有問題的。

03、瞻前顧后

在一個(gè)測(cè)試用例中,可能要對(duì)多個(gè)方法進(jìn)行測(cè)試。在測(cè)試之前呢,需要準(zhǔn)備一些條件,比如說創(chuàng)建對(duì)象;在測(cè)試完成后呢,需要把這些對(duì)象銷毀掉以釋放資源。如果在多個(gè)測(cè)試方法中重復(fù)這些樣板代碼又會(huì)顯得非常啰嗦。

這時(shí)候,該怎么辦呢?

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

  1. public class Calculator { 
  2.     public int sub(int a, int b) { 
  3.         return a - b; 
  4.     } 
  5.     public int add(int a, int b) { 
  6.         return a + b; 
  7.     } 

新建測(cè)試用例的時(shí)候記得勾選setUp 和 tearDown。

 

生成后的代碼如下所示。

  1. class CalculatorTest { 
  2.     Calculator calculator; 
  3.  
  4.     @BeforeEach 
  5.     void setUp() { 
  6.         calculator = new Calculator(); 
  7.     } 
  8.  
  9.     @AfterEach 
  10.     void tearDown() { 
  11.         calculator = null
  12.     } 
  13.  
  14.  
  15.     @Test 
  16.     void sub() { 
  17.         assertEquals(0,calculator.sub(1,1)); 
  18.     } 
  19.  
  20.     @Test 
  21.     void add() { 
  22.         assertEquals(2,calculator.add(1,1)); 
  23.     } 

@BeforeEach 的 setUp() 方法會(huì)在運(yùn)行每個(gè) @Test 方法之前運(yùn)行;@AfterEach 的tearDown() 方法會(huì)在運(yùn)行每個(gè) @Test 方法之后運(yùn)行。

與之對(duì)應(yīng)的還有 @BeforeAll 和 @AfterAll,與 @BeforeEach 和 @AfterEach 不同的是,All 通常用來初始化和銷毀靜態(tài)變量。

  1. public class DatabaseTest { 
  2.     static Database db; 
  3.  
  4.     @BeforeAll 
  5.     public static void init() { 
  6.         db = createDb(...); 
  7.     } 
  8.      
  9.     @AfterAll 
  10.     public static void drop() { 
  11.         ... 
  12.     } 

03、異常測(cè)試

對(duì)于 Java 程序來說,異常處理也非常的重要。對(duì)于可能拋出的異常進(jìn)行測(cè)試,本身也是測(cè)試的一個(gè)重要環(huán)節(jié)。

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

  1. public class Factorial { 
  2.     public static long fact(long n) { 
  3.         if (n < 0) { 
  4.             throw new IllegalArgumentException("參數(shù)不能小于 0"); 
  5.         } 
  6.         long r = 1; 
  7.         for (long i = 1; i <= n; i++) { 
  8.             r = r * i; 
  9.         } 
  10.         return r; 
  11.     } 

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

  1. @Test 
  2. void factIllegalArgument() { 
  3.     assertThrows(IllegalArgumentException.class, new Executable() { 
  4.         @Override 
  5.         public void execute() throws Throwable { 
  6.             Factorial.fact(-2); 
  7.         } 
  8.     }); 

我為你提供了一個(gè) assertThrows() 的方法,第一個(gè)參數(shù)是異常的類型,第二個(gè)參數(shù) Executable,可以封裝產(chǎn)生異常的代碼。如果覺得匿名內(nèi)部類寫起來比較復(fù)雜的話,可以使用 Lambda 表達(dá)式。

  1. @Test 
  2. void factIllegalArgumentLambda() { 
  3.     assertThrows(IllegalArgumentException.class, () -> { 
  4.         Factorial.fact(-2); 
  5.     }); 

04、忽略測(cè)試

有時(shí)候,由于某些原因,某些方法產(chǎn)生了 bug,需要一段時(shí)間去修復(fù),在修復(fù)之前,該方法對(duì)應(yīng)的測(cè)試用例一直是以失敗告終的,為了避免這種情況,我為你提供了 @Disabled 注解。

  1. class DisabledTestsDemo { 
  2.  
  3.     @Disabled("該測(cè)試用例不再執(zhí)行,直到編號(hào)為 43 的 bug 修復(fù)掉"
  4.     @Test 
  5.     void testWillBeSkipped() { 
  6.     } 
  7.  
  8.     @Test 
  9.     void testWillBeExecuted() { 
  10.     } 
  11.  

@Disabled 注解也可以不需要說明,但我建議你還是提供一下,簡(jiǎn)單地說明一下為什么這個(gè)測(cè)試方法要忽略。在上例中,如果團(tuán)隊(duì)的其他成員看到說明就會(huì)明白,當(dāng)編號(hào) 43 的 bug 修復(fù)后,該測(cè)試方法會(huì)重新啟用的。即便是為了提醒自己,也很有必要,因?yàn)闀r(shí)間長了你可能自己就忘了,當(dāng)初是為什么要忽略這個(gè)測(cè)試方法的。

05、條件測(cè)試

有時(shí)候,你可能需要在某些條件下運(yùn)行測(cè)試方法,有些條件下不運(yùn)行測(cè)試方法。針對(duì)這場(chǎng)使用場(chǎng)景,我為你提供了條件測(cè)試。

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

  1. @Test 
  2. @EnabledOnOs(MAC) 
  3. void onlyOnMacOs() { 
  4.     // ... 
  5.  
  6. @TestOnMac 
  7. void testOnMac() { 
  8.     // ... 
  9.  
  10. @Test 
  11. @EnabledOnOs({ LINUX, MAC }) 
  12. void onLinuxOrMac() { 
  13.     // ... 
  14.  
  15. @Test 
  16. @DisabledOnOs(WINDOWS) 
  17. void notOnWindows() { 
  18.     // ... 

2)不同的 Java 運(yùn)行環(huán)境,可能也需要不同的測(cè)試用例。@EnabledOnJre 和 @EnabledForJreRange 注解就可以滿足這個(gè)需求。

  1. @Test 
  2. @EnabledOnJre(JAVA_8) 
  3. void onlyOnJava8() { 
  4.     // ... 
  5.  
  6. @Test 
  7. @EnabledOnJre({ JAVA_9, JAVA_10 }) 
  8. void onJava9Or10() { 
  9.     // ... 
  10.  
  11. @Test 
  12. @EnabledForJreRange(min = JAVA_9, max = JAVA_11) 
  13. void fromJava9to11() { 
  14.     // ... 

06、尾聲

最后,給你說三句心里話吧。在編寫單元測(cè)試的時(shí)候,你最好這樣做:

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

2)每個(gè)單元測(cè)試應(yīng)該互相獨(dú)立,不依賴運(yùn)行時(shí)的順序。

3)測(cè)試時(shí)要特別注意邊界條件,比如說 0,null,空字符串"" 等情況。

希望我能盡早的替你發(fā)現(xiàn)代碼中的 bug,畢竟越早的發(fā)現(xiàn),造成的損失就會(huì)越小。see you!

 本文轉(zhuǎn)載自微信公眾號(hào)「沉默王二」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系沉默王二公眾號(hào)。

 

責(zé)任編輯:武曉燕 來源: 沉默王二
相關(guān)推薦

2017-11-22 15:13:20

集成測(cè)試自動(dòng)化測(cè)試契約測(cè)試

2023-09-14 12:03:30

空指針判空

2021-06-09 06:41:11

OFFSETLIMIT分頁

2020-12-04 10:05:00

Pythonprint代碼

2023-10-26 16:33:59

float 布局前段CSS

2021-05-25 09:30:44

kill -9Linux kill -9 pid

2021-01-29 11:05:50

PrintPython代碼

2020-12-02 11:18:50

print調(diào)試代碼Python

2020-12-03 09:05:38

SQL代碼方案

2020-12-15 08:06:45

waitnotifyCondition

2024-12-26 07:47:20

2022-01-27 07:48:37

虛擬項(xiàng)目Django

2020-07-17 07:15:38

數(shù)據(jù)庫ID代碼

2012-03-13 16:14:09

JavaJUnit

2020-02-05 14:17:48

Python數(shù)據(jù)結(jié)構(gòu)JavaScript

2020-02-05 16:37:06

方括號(hào)Python方法

2022-03-10 10:12:04

自動(dòng)化腳本Bash

2019-03-12 14:48:29

路由器XBOXPS4

2022-10-27 21:34:28

數(shù)據(jù)庫機(jī)器學(xué)習(xí)架構(gòu)

2009-06-19 16:26:51

JUnit測(cè)試骨架
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)