在Java生態圈裏談到單元測試,第一時間應該都會想到JUnit,它誕生於1997年,幾乎是與Java同樣古老的存在,然而,後續發展似乎也如同Java同樣的緩慢。

自2006年JUnit 4發布至今,已過了11年,JUnit 5才終於在9月10日正式釋出了!這是個值得期待的新測試框架嗎?看了看使用手冊,JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage?這啥?

Vintage不是重寫的JUnit 4

無論如何,自JUnit 4發表以來的這些日子,許多專案也基於它寫了不少測試,無論JUnit 5再怎麼好,總不能棄既有的JUnit 4測試不顧,相容性是必然的考量,就結論而言,既有的JUnit 4測試基本上不用修改,JUnit 5可以執行JUnit 4的測試!

現階段的JUnit 5,提供了Gradle、Maven,以及主控臺的方式,來運行測試。

以Gradle建構為例,可在build.gradle中,加入JUnit Platform的plugin:

buildscript {
           repositories {
                   mavenCentral()
}
dependencies {
        classpath 'org.junit.platform:junit-platform-gradle-plugin:1.0.0'
}
}
apply plugin: 'org.junit.platform.gradle.plugin'

然後,繼續設定Vintage引擎,例如,在build.gradle原本的testCompile("junit:junit:4.12")底下,加入了testRuntime("org.junit.vintage:junit-vintage-engine:4.12.0"),接著,執行gradle junitPlatformTest,JUnit 5就可以執行既有的JUnit 4測試了。

那麼,JUnit Vintage是為了相容性,而特別開發的新測試框架嗎?不是!實際上執行測試時,使用的還是使用既有的JUnit 4框架。

所以,Vintage是JUnit 5為了JUnit 4,而特別寫的執行器嗎?不是!「JUnit 5可以執行JUnit 4的測試」的說法,並不精確,而是JUnit 5提供了Vintage引擎實作,透過這個引擎,JUnit Platform可以運行JUnit 4測試框架撰寫的測試。

Jupiter才是新的測試框架

解決了相容性的問題,是該看看JUnit 5在撰寫測試時有什麼不同了。在〈JUnit 5 User Guide〉(https://goo.gl/eEBD2q)的〈3. Writing Tests〉中,可以看到許多的範例,像是測試方法不須是public,方法預設權限就夠了,有著各種新的標註(Annotation)、斷言(Assertion)方法,以及假定(Assumption)方法,相關的標註、斷言與前置API,都是放在org.junit.jupiter.api的套件名稱之下。

而且,在JUnit 5中,若要談到測試框架,JUnit Jupiter才是精確的說法。

為了要能使用Jupiter測試框架,來撰寫與運行測試,build.gradle除了加入JUnit Platform的plugin之外,還必須在dependencies之中,加入testCompile("org.junit.jupiter:junit-jupiter-api:5.0.0"),以及testRuntime("org.junit.jupiter:junit-jupiter-engine:5.0.0"),前者是Jupiter測試框架的實作,後者是Jupiter引擎實作,它讓JUnit Platform可以運行Jupiter撰寫的測試。

同樣地,執行gradle junitPlatformTest,就可以運行測試了,如果先前已經設定好Vintage引擎,那麼,JUnit 4與Jupiter撰寫的測試都會執行。不過,我在玩弄測試的過程中,因為隨意命名測試類別而犯了個低級的錯誤——若JUnit 4與Jupiter的測試都要能執行,預設JUnit 4類別名稱必須是Test或Tests結尾,不然,它只會跑Jupit er的測試類別。

在〈3. Writing Tests〉還可以看到幾個亮點,像是斷言與假定等API都支援了Java 8的Lambda運算式,也許你會對假定感到好奇,兩者的差別在於,不成立的斷言會讓測試失敗,而假定不成立的話,就不會繼續執行指定的測試;另外,支援巢狀測試、依賴注入、動態測試等,都是Jupiter中的重要特色。

Platform是測試與工具的橋樑

對於JUnit Platform來說,JUnit 4與Jupiter單純就只是個別的測試框架,只要提供各自的junit-platform-engine實作,junit-platform-launcher就會知道怎麼執行它們。

技術面上,前述的Vintage引擎與Jupiter引擎,各自實作了org.junit.platform.engine.TestEngine介面,而內建的org.junit.platform.console.ConsoleLauncher,可在主控臺啟動Platform,透過Vintage或Jupiter引擎來運行測試。

因此,若有開發者想要這麼做,可以開發TestNG的TestEngine實作,並向Platform註冊(https://goo.gl/ZM2DeG),那麼,Platform就能夠運行TestNG撰寫的測試了,也就是說,若不打算用Jupiter,也可以繼續使用TestNG或JUnit 4,來撰寫測試,然後使用Platform來運行測試。

既然要繼續使用TestNG或JUnit 4來撰寫、執行測試,那又何必多一層Platform?

若單純把JUnit 5看成是新的測試框架,確實是多此一舉,不過,Platform主要是作為工具與測試框架之間的標準,如果建構工具、整合開發工具或CI伺服器等支援Platform,那麼,只要測試框架提供TestEngine實作,這些工具就可以執行該測試框架撰寫的測試,這麼一來,各個測試框架就不用為了讓工具能支援,而針對不同的工具撰寫特定的實作。

當然,真的看完Jupiter而產生愛的話,Platform還是可以當遷移過程的共同平臺,可先讓既有的測試運行在Platform上,然後將測試逐步修改為使用Jupiter。

因此,就這個機制看來,如果TestNG真的提供了TestEngine實作,也可以逐步在Platform上,將TestNG的測試改為Jupiter,或者反過來地,你對JUnit失望了,在Platform上,把JUnit 4、5的測試改至TestNG也行。

不只是新的測試框架

現在,可以重新來看看JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage了。

在這當中,JUnit 5是專案名稱,JUnit Jupiter是指Jupiter測試框架與Jupiter引擎實作,而JUnit Vintage是指Vintage引擎實作,若開發時的相關工具、其他測試框架,想取得Platform的支援,可以實作或使用JUnit Platform中,所規範的標準介面與類別。

在〈Introducing JUnit 5〉(https://goo.gl/4UKpJX)中,有張JUnit 5的架構圖,就清楚地描繪出,測試框架與開發工具等共同依賴在Platform的概念,這同樣是JUnit 5的目標,也就是「協助開發者撰寫測試」以及「協助工具運行測試」,而不單純只是考慮與JUnit 4的相容性,以及新測試框架的功能性。

雖然將來大家對於JUnit Platform的部份要不要買單,是另外一回事,然而,就撰寫測試而言,畢竟JUnit 4.12自2014年底釋出,到現在又快三年了,若你對此感到不耐,也許可以看一眼Jupiter,裏頭有不少東西值得試試看,暫且把JUnit 5當成是個相容於JUnit 4的新測試框架來使用,雖不精確,倒也不失為一種選擇。

專欄作者

熱門新聞

Advertisement