1. 程式人生 > >升級到JUnit5的7個理由

升級到JUnit5的7個理由

翻譯:叩丁狼教育吳嘉俊

“不進,則退”——約翰·沃爾夫岡·馮·歌德

最新版本的JUnit在2017年的第三季度已經發布了final release版本。大量的里程碑改進加入了新版本中。我希望你能夠儘快的使用起來,這篇文章的主題,我列出了7個點,鼓勵大家立刻開始去玩玩JUnit5.

立刻可用

當一門語言、一款應用伺服器或程式碼庫的新版本出現的時候,大部分開發人員往往會等到業界真正開始推行這個產品的時候,才開始選擇學習。這種情況經常出現,看看JavaScript,在ES2015規範釋出兩年後,大部分主流Web瀏覽器仍在努力支援所有語言特徵。雖然JS轉化器在一定程度上推動了標準的實施,但是確實是饒了一個彎路。

JUnit5團隊從一開始就考慮了新版本的這些問題。IntelliJ IDEA已經支援IDE中執行JUnit5測試,Eclipse也將支援JUnit5列入了其beta support列表中。兩款最流行的構建工具,Maven和Gradle,已經支援JUnit5測試。如果你不熟悉這些流行的工具,JUnit5也提供了一款控制檯工具幫你自動的執行JUnit5測試。就算這些都不成立,還有一個JUnit4的測試執行工具能夠在JUnit4環境中測試新版本的測試程式碼。你看看,所有的配套都準備好了。

易於學習

第一眼看上去,新的API(新命名為JUnit Jupiter)和他的前身非常像。這個相似程度之高,甚至你會覺得這個新的版本就是新瓶裝舊酒。包括最流行的那些斷言方法也是一樣的。唯一的區別就是放在了不同的包中,並且引數的順序有了一些區別。一些JUnit4中的著名的註解修改了名字,但是作用都是差不多的。如果你對JUnit4比較熟悉,那麼你要掌握JUnit5的基礎使用,只需要幾分鐘。

這個時候,你可能會想,既然兩個版本這麼像,那為啥我還要升級呢?簡單來說,JUnit5更像是JUnit4的一個超集,他提供了非常多的增強。

全新的功能

一旦你熟悉了JUnit5的基礎內容,你可以順利進入下一個階段,根據你的需求,你可能會考慮以下幾個進階點:

  • 新的斷言
  • 巢狀測試類 - 不僅僅是BDD(Behavior Driven Development)開發人員大力推崇
  • 動態測試 - 在執行時生成測試用例
  • 擴充套件測試 - 允許你重新定義測試的行為,允許你使用外掛的形式重用你的測試類。

解決了前序版本的問題

沒有完美的東西,JUnit4也一樣。在JUnit4中,在社群中對幾個大大小小的問題都有各種討論和改進,我們來看看這些問題。

異常驗證

異常檢查可以說是JUnit4中一個標誌性的問題。我們來考慮一下下面這個JUnit4測試:

@Test(expected = IllegalArgumentException.class)
public void shouldThrowException() throws Exception {
    Task task = buildTask();
    LocalDateTime oneHourAgo = LocalDateTime.now().minusHours(1);
    task.execute(oneHourAgo);
}

想象我們執行這個測試,如果傳入到execute()方法中的引數是一個過去的時間,會正常丟擲一個IllegalArgumentException異常。這種情況下,測試會執行的正確。但是如果在buildTask()方法中丟擲一個異常呢?測試會正常執行,並且會提示你得到的異常和期望異常不匹配。這裡,問題就出來了,我們只是希望測試在指定位置得到指定的異常,而不是在整個測試體中出現的異常,都作為對比異常。在JUnit5中,提供了一個assertThrows()方法,可以非常輕鬆的處理這個問題:

@Test
void shouldThrowException() throws Exception {
    Task task = buildTask();
    LocalDateTime oneHourAgo = LocalDateTime.now().minusHours(1);
    assertThrows(IllegalArgumentException.class,
                 () -> task.execute(oneHourAgo));
}

超時時間測試

額外的,添加了一組新的斷言,用於在lambda樣式斷言中衡量程式碼超時時間,在這種情況下,就不會擔心測試的setup階段對程式碼執行時間的影響,你可以指定只去衡量某一段程式碼的執行時間。另外,還提供了一個選項,當已經出現超時的時候,你是選擇停止應用執行或者是繼續當前測試以衡量程式碼執行的真實完整時間。

@Test
void shouldTimeout() throws Exception {
    ExpensiveService service = setupService();
    assertTimeout(ofSeconds(3), () -> {
        service.expensiveMethod();
    });
}

引數化測試

在JUnit4中,如果想要實現引數化測試(使用不同的引數來測試相同的一個方法),只能使用測試類中的欄位來實現,在JUnit5中,提供了引數化測試來實現這個需求。不同的引數值可以直接和一個測試方法關聯,並且允許直接在一個測試類中提供不同的引數值直接參與測試,這些在JUnit4中,都是不可能實現的。

測試的引數可以通過一組CSV格式的字串,外部的CSV檔案,列舉,工廠方法,或者指定的提供類來提供。CSV中的字串型別的值可以自動的轉化為指定的型別,並且你可以完成自己的型別轉換器,將String轉成你希望的任何指定型別。

多執行器

JUnit4中最讓人詬病的可能就是不能使用多個測試執行器測試。引數化測試或者巢狀測試的問題,可以通過測試執行器來解決,但問題是我們只能使用一個測試執行器。比如,在Spring4.2之前,每一個依賴於Spring上下文的測試都只能使用SpringJUnit4ClassRunner,在4.2之後,你可以使用專用的規則來(SpringClassRule)處理這個問題,允許你使用不同的測試執行器來執行測試,但是同一時間,仍然只能使用一次。JUnit4從一開始因為相容問題,就根本沒有設計執行器擴充套件的問題。JUnit 5團隊選擇了一條不同的路徑,並計劃了新版本的架構來解決這個問題。

遷移簡單

到這裡,你是否又擔心在目前的專案中已經有上百個測試用例,為了獲得JUnit5的好處,而不得不重寫這些測試程式碼?請放鬆,JUnit5團隊提供了多種升級的選擇。第一點,你可以在一個專案中同時使用JUnit4和JUnit5,而不用擔心出現衝突。當你新增新的JUnit依賴到專案中,有兩種方式可以同時執行測試。

侵略性最小的方式是使用JUnit5 API完成新的測試,並且使用專用的JUnitPlatform執行器在JUnit4下執行。但是,剛才我們不是才說JUnit4的癥結是執行器麼?當然,這個JUnitPlatform是一個受限的執行器,他只能支援新版本的一部分功能。但是,這仍然是一個開始使用新版本API的不錯的開始。最快的學習方法就是在真實專案中使用,對吧。

第二種方式,是將JUnit5作為主要的測試執行器。除了能獲取框架帶來的諸多優勢,你也可以在新的平臺中執行老版本的測試,當然有一些限制。由於設計概念上的區別,JUnit4中只有部分的一些特效能夠在JUnit5中執行。在你邁進JUnit5之前,先了解一下這些限制:http://junit.org/junit5/docs/current/user-guide/#migrating-from-junit4-rulesupport

做貢獻的機會

因為新版本還在持續開發過程中,這個時候提交一些有用的想法,是非常好的機會。框架的使用者,你期望在專案中能用到哪些新的功能,每一個有用的想法都可以通過issue提交到JUnit5專案中。

但是,當你提交一個新的issue的時候,請確保這個想法在框架中確實沒有實現。儘量不要為團隊開發者去處理已經存在的issue或者沒有太多價值的issue。但是,如果你有一個好的建議,不要猶豫,請提交一個issue。對開源的貢獻一定不是僅僅只侷限於程式碼提交,貢獻idea同樣重要。JUnit5的issues列表:https://github.com/junit-team/junit5/issues

JVM測試

JUnit5的目標,不僅僅只是瞄準測試框架。JUnit5的重大改進不僅僅只是打破了常規的API,或者引入了新的擴充套件模型。一個非常重要的目標,是打造一個基於JVM測試框架的基礎平臺。這意味著什麼?我們來看一個圖:

img

你可以看到,就像洋蔥一樣,JUnit5的架構也是分層的。最核心的就是基礎平臺。IDE和構建工具都是作為客戶端和這個核心平臺互動,以達到在專案中執行測試的目的。TestEngine的實現在平臺中用於發現和執行測試,並且輸出測試報告,並通過核心平臺返回給客戶端。

核心關注點是擴充套件能力,但並不僅僅只是存在於測試類級別。在整個測試平臺級別,都提供了足夠的擴充套件能力。任何一個框架都可以在JUnit平臺上執行他自己的測試,只需要提供框架本身對TestEngine介面的實現即可。只需要一點點工作,通過這一個擴充套件點,框架就能得到所有IDE和構建工具在測試上的支援。這對於新框架來說絕對是好事,在測試和構建這塊的門檻更低。

這些對於一個開發者來說意味著什麼呢?這意味著一個測試框架和JVM開發市場上所有主流的工具整合的時候,你能更容易的說服你的經理,開發leader,或者不管是誰阻礙你引入這個測試框架的人。 JUnit Vintage就是一個TestEngine實現,用於執行JUnit4的測試。

相信以上的點,足以說服你,開始從一個新的測試類開始,使用JUnit5。