原文地址:http://blog.codefx.org/design/architecture/junit-5-architecture/
原文日期:29, Mar, 2016
譯文首發:Linesh 的博客:JUnit 5 系列:架構體系
個人 Github:http://github.com/linesh-simplicityhtml
如今,咱們已經知道了 如何配置 JUnit 5 環境 及 如何寫一些測試,接下來就來看一點封面下的內容吧。本篇咱們將討論 JUnit 5 的架構體系,以及它之成形如此的緣由。前端
本文章是這個 JUnit 5 系列的一部分:java
(若是不喜歡看文章,你能夠戳這裏看個人演講,或者看一下最近的 vJUG 講座,或者我在 DevoxxPL 上的 PPT。git
本系列文章都基於 Junit 5發佈的先行版 Milestone 2。它可能會有變化。若是有新的里程碑(milestone)版本發佈,或者試用版正式發行時,我會再來更新這篇文章。github
這裏要介紹的多數知識你均可以在 JUnit 5 用戶指南 中找到(這個連接指向的是先行版 Milestone 2,想看的最新版本文檔的話請戳這裏),而且指南還有更多的內容等待你發掘。下面的全部代碼均可以在 個人 Github 上找到。api
除了 Hamcrest,JUnit 4沒有任何外部依賴,其全部的功能都被打包在一個構件(artifact)中。這徹底違反了單一職責原則,它被提供給開發者、IDE、構建工具、其餘測試框架、其餘擴展等使用,不一樣的使用者,依賴的都是一個一樣的構件。架構
而在這其中,只有開發者能——或者說曾經能——以最乾淨的方法來使用它。他們一般只須要 JUnit 的公共 API,不須要管其餘的。很是好。框架
但生態圈中的其餘成分則不是這樣使用 JUnit:測試框架、擴展,特別是 IDE 和構建工具的開發者,他們須要深刻到 JUnit 的深處,到它的細枝末節:非 public 的類、內部 API,甚至 private 字段。它們的正常工做極大地依賴於 JUnit 的實現細節。這使得 JUnit 維護團隊不能輕易地修改框架的這些內部實現,所以團隊的開發進度受到了很大的影響。ide
固然,這些工具的開發者們也並不是有意爲之。爲了實現那些咱們十分喜好的特性,他們不得不使用內部的 API,由於 JUnit 4 並無提供相應的 API:一個強大到足以知足工具開發者們需求的 API。工具
Junit Lambda 團隊開始着手於 JUnit 5 的開發,但願能讓這一切變得明朗起來。
退一步想,咱們不難辨識出,這裏至少有兩個不一樣的關注點須要分離:
再仔細思考一下第二點,咱們可能會問,「哪些測試?」這個固然是指 Junit 測試。「我知道,但具體是哪些版本的測試呢?」呃…「還有,具體是指什麼類型的測試?」好吧,你讓我給你……「只能跑那些老版本的 @Test
註解的測試麼?有沒有其餘新的方法來運行測試呢?……」行行行,都給我閉嘴!聽我講着。
爲了進一步將待識別測試的類型 與 實際運行它們 這兩個關注點解耦,上面的第二點須要細分:
識別出這兩個關注點之後,「做爲平臺的 JUnit 」(用於運行咱們的測試)和「做爲工具的 JUnit 」(用於撰寫咱們的測試)這兩個概念的分離就清晰了。爲了完成這個完全的分離,JUnit 團隊決定將 JUnit 5 分紅三個子項目:
JUnit Jupiter
包含了咱們用於撰寫測試的 API(關注點1),以及一個能理解測試代碼的引擎(關注點2.1)。
JUnit Platform
提供了一套統一的 API 以運行測試,及基於 API 之上的一套工具(關注點2.2和2.3)。
JUnit Vintage
提供了一套引擎,用以在 JUnit 5 中運行 JUnit 3 和 JUnit 4 的測試(關注點2.1)。
JUnit 5 的架構體系徹底是遵循這個關注點分離思想的產物:
junit-jupiter-api(1)
開發者用於撰寫測試的 API,包含了咱們在JUnit 5 的基礎知識一節中所說起的全部註解、斷言等。
junit-platgorm-engine(2.3)
包含了一套全部測試引擎都必須實現的 API。這樣,不一樣的測試引擎之間能夠經過統一的接口被調用。引擎能夠跑正常的 JUnit 測試,但也能夠實現不一樣的引擎用以執行其餘框架寫成的測試,如 TestNG、Spock、Cucumber 等。
junit-jupiter-engine(2.1)
junit-platform-engine API 的一個實現,專門用於執行 JUnit 5 撰寫的測試。
junit-vintage-engine(2.1)
junit-platform-engine API 的一個實現,專門用於執行 JUnit 3 或 JUnit 4 撰寫的測試。過去,JUnit 4 的構件 junit-4.12 充當了兩個角色:它既是開發人員用於實現測試的 API,又包含了用以執行測試的核心組件。這個引擎,能夠認爲是低版本的 JUnit 3/4 與 JUnit 5 之間的一個適配器。
junit-platform-launcher(2.2)
這部分使用了一個服務加載器 ServiceLoader
來發現測試引擎,並協調不一樣實現之間的執行。它提供了一個 API 給 IDE 和構建工具,使得它們可以與測試執行過程交互,好比,運行單個的測試、蒐集測試結果並展現等。
聽起來怎樣,很酷吧。
這部分架構對於咱們生態鏈前端的使用者來講基本是透明的。咱們的項目只須要引入一個用於編寫測試的 API 依賴,其他的組件讓工具去操心便可。
如今來講說那些你們都在使用的內部 API。JUnit 5 團隊但願這個問題也能獲得解決,爲此給 JUnit 的 API 設立了生命週期。這裏,我將源碼中給出的部分解釋截取於此。
內部 API(internal)
不容許被 JUnit 開發者以外的任何人使用。這部分 API 可能被移除,而且不會事先通知。
已過期(Deprecated)
不該該再被使用的 API,它們可能在下次小版本發佈時被移除。
實驗階段(Experimental)
爲一些新的、實驗階段的特性所使用的 API,這些新特性可能會或已經被公開使用並接受反饋中。
可使用,但要謹慎。這些 API 將來可能被提高至 維護中 或 穩定 級別,但也可能不帶提早通知就被移除。
維護中(Maintained)
使用該 API 的特性,至少在該大版本的下一個小版本發佈時不會發生向後不兼容的改變。若是將來有移除維護中 API 的計劃,它會先被打回到 已過期 階段。
穩定(Stable)
使用該 API 的特性,至少在下個大版本發佈以前不會發生向後不兼容的改變。
JUnit 對外公開的類都帶有一個 @API(usage)
註解,其中 usage
是上面幾個值中的其中一個。團隊但願這能給 API 的調用方以充足的信息,即他們所使用的 API 處於什麼生命週期中,同時,也但願給每一個團隊以自由,讓他們決定是否改變或移除過期 API 。
其實還有一件事。Junit 5 的體系結構使得 IDE 和構建工具可以將其做爲中間層,以運行全部類型的測試框架(前提是該框架實現了其對應的引擎)。這樣的話,工具自己就不須要去實現框架相關的測試支持,它們只須要使用一套統一的藉口,便可實現測試發現、測試執行和結果收集。
是嘛,真的能夠嗎?
失敗的測試,一般使用異常來描述。但不一樣的測試框架和斷言庫之間並沒有一個統一的接口。相反,它們一般實現了各自不一樣的版本(常見的是繼承 AssertionError
或 RuntimeException
)。這就使得不一樣框架間的互操做變得更加複雜,也使得工具之間沒法簡單使用一套統一的接口。
爲了解決這個問題,Junit Lambda 團隊又分出來一個獨立的項目,The Open Test Alliance for the JVM。這是它們的提議:
基於 JUnit Lambda 團隊近來與來自Eclipse、Gradle 及 Intellij 等 IDE 和構建工具開發者所展開的討論,咱們呼籲要創建這樣一個開源項目:它用於提供一套基於 JVM的 測試庫與測試框架 間的最小公共接口集。
項目主要目標是,爲各測試框架(如 JUnit、TestNG、Spock 等)和三方斷言庫(Hamcrest、Assert 等)提供一個公共的異常集合。有了這個集合,IDE 和構建工具就能夠一個統一的接口對全部測試過程——如對失敗斷言、失敗假言斷定的處理、對測試執行過程的可視化、在 IDE 中生成測試結果報告等——進行處理。
截止目前,該項目的呼籲彷佛並未引發太多重視,或說是基本未獲得重視。若是你以爲這是個好的想法,你能夠經過一些方式來支持,好比向你常用的測試框架維護者發出聲音。
本篇咱們介紹了 JUnit 5 的架構設計,它將原有的 API 分紅了兩部分:編寫測試部分的 API 和 執行測試的引擎。這個引擎進一步地被切分紅三個部分:一個解析測試代碼的 API、一個測試執行器(launcher),和一些支持不一樣測試框架的引擎實現。這樣開發者只須要爲項目引入 API 部分的依賴(用於編寫測試),而測試框架的開發者們則只須要實現引擎部分的 API(其餘工做已經由 JUnit 處理了),構建工具方面也只須要實現 launcher API以協調測試執行。
下篇文章將會介紹 JUnit 5 的可拓展性。敬請期待。