著名的Java單元測試框架Junit 4已經出來很長時間了,當時我發現JUnit 5已經處于測試版,就準備寫文章來介紹JUnit 5.不過因為還是測試版,所以有些地方還不太完善,我也有點懶沒有好好寫。這幾天突然想起這事了,在到官網上查看,發現就在9月10日,JUnit 5的正式版終于出來了!那么我就正好把文章重新好好寫寫,為大家介紹這個最新的JUnit框架。
框架結構
和JUnit 4相比,JUnit 5的結構非常清晰,為自定義插件、IDE測試執行等擴展功能做了很好的支持。這一點從項目結構就可以看出來。
JUnit Platform
這一組的包名是org.junit.platform,從名字就可以看到,這一組的主要功能就是作為測試框架的基礎平臺。這個包下的模塊包含基礎API、執行引擎及執行器、基本的命令行執行功能、命令行界面、Maven及Gradle的測試插件等最基本的功能。
JUnit Jupiter
Jupiter 是JUnit 5的代號,這個包下的模塊包含JUnit 5的主要功能。如果我們要使用JUnit 5,那么必然要包含這一組模塊。
JUnit Vintage
Vintage 是舊版本JUnit 的代號,這個包下的模塊可以讓我們在新的JUnit平臺上運行舊的JUnit 3 和 4 的測試。
導入類庫
在JUnit 5還在測試階段的時候,官方文檔上還有在Maven和Gradle中集成JUnit 5的例子。但是到了正式版,這一部分的內容消失了,僅僅留下兩個示例項目的鏈接,讓我們自己參考(復制粘貼)。
使用Maven
junit5-maven-consumer 是官方的Maven例子。本來我準備把相關的POM配置貼到這里,但是一看Maven的配置太長了,所以還是算了。如果有需求的話請自己查看這個項目的POM配置。
使用Gradle
如果用Gradle的話,那么這個問題就簡單多了。在junit5-gradle-consumer 示例項目中也有比較詳細的說明。
首先,Gradle默認不支持JUnit 5,,所以需要啟用JUnit Platform Gradle 插件來支持。
1
2
3
4
5
6
7
8
9
|
buildscript { repositories { mavenCentral() } dependencies { classpath 'org.junit.platform:junit-platform-gradle-plugin:1.0.0' } } apply plugin: 'org.junit.platform.gradle.plugin' |
然后是關于這個Gradle插件的配置。默認情況下所有的引擎和標簽都會被執行。如果你想選擇只執行某些引擎和標簽的測試,可以取消下面的注釋并按照你自己的需求進行修改。當然假如你沒有這些高級需求,可以把這一部分刪掉。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
junitPlatform { // platformVersion '1.0.0' filters { engines { // include 'junit-jupiter', 'junit-vintage' // exclude 'custom-engine' } tags { // include 'fast' exclude 'slow' } // includeClassNamePattern '.*Test' } // enableStandardTestTask true // reportsDir file('build/test-results/junit-platform') // this is the default // logManager 'org.apache.logging.log4j.jul.LogManager' } |
如果你只需要運行JUnit 5測試,只需要導入下面兩個依賴項。JUnit Platform的依賴會自動導入。
1
2
3
4
|
dependencies { testCompile( "org.junit.jupiter:junit-jupiter-api:5.0.0" ) testRuntime( "org.junit.jupiter:junit-jupiter-engine:5.0.0" ) } |
如果你想在新平臺下運行舊的JUnit 3和4測試,需要導入下面的依賴項。
1
2
3
4
|
dependencies { testCompile( "junit:junit:4.12" ) testRuntime( "org.junit.vintage:junit-vintage-engine:4.12.0" ) } |
編寫測試
JUnit 4測試
如果前面都配置好了,現在就可以開始編寫測試了。首先先來復習一下舊的JUnit 4測試。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
public class JUnit4Test { @BeforeClass public static void init() { System.out.println( "Before Class" ); } @AfterClass public static void clean() { System.out.println( "After class" ); } @Before public void before() { System.out.println( "Before" ); } @After public void after() { System.out.println( "After" ); } @Test public void test1() { System.out.println( "Test 1" ); } @Test public void test2() { System.out.println( "Test 2" ); } } |
使用gradle test等命令執行一下,就會執行這個測試。結果類似于這樣。
1
2
3
4
5
6
|
Before Class Before Test 1 Test 2 After After class |
JUnit 5測試
讓我們來看看等效的JUnit 5測試怎么寫。可以看到最明顯的變化:首先幾個注解被重新命名成更見名知義的名稱;另外一點是測試方法不必是公有方法,這樣我們可以少敲幾下鍵盤。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
public class JUnit5Test { @BeforeAll static void beforeAll() { System.out.println( "Before All" ); } @AfterAll static void afterAll() { System.out.println( "After All" ); } @BeforeEach void before() { System.out.println( "Before" ); } @AfterEach void after() { System.out.println( "After" ); } @Test void test1() { System.out.println( "Test 1" ); } @Test void test2() { System.out.println( "Test 2" ); } } |
編寫斷言
為了驗證測試用例是否正確,我們需要編寫一些斷言。JUnit 5自帶了很多斷言,可以幫助我們編寫測試用例。而且這些斷言都帶有可以接受lambda表達式的重載版本,非常適合Java 8使用。當然我個人認為斷言還是AssertJ更方便一點。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
import static org.junit.Assert.assertTrue; import static org.junit.jupiter.api.Assertions.*; public class AssertionDemo { @Test void testAssertion() { assertEquals( 10 , 10 ); assertTrue( true ); assertEquals( 100 , 100 , "兩個數相等" ); assertAll( "數字" , () -> assertEquals( "name" , "name" ) , () -> assertEquals( 500 , 500 )); assertThrows(InvalidParameterException. class , () -> { throw new InvalidParameterException(); } ); int result = assertTimeout(Duration.ofSeconds( 5 ) , () -> { int i = 0 , j = 0 ; while (i <= 100 ) { for (; j <= 100000 ; j++) j++; i++; } return i; }); assertEquals( 100 , result); } } |
依賴注入
現在測試類的構造方法和測試方法都可以接受參數了。ParameterResolver接口定義了如何在運行時注入參數的方法。內置的幾個可以讓我們獲取測試用例運行時的信息。
首先是TestInfoParameterResolver。如果方法上有TestInfo類型的實例,JUnit 5框架就會自動注入該實例,這個實例的幾個方法可以讓我們獲取測試類和測試方法的名稱、顯示名稱、標簽等信息。
1
2
3
4
5
6
7
8
9
|
public class DependencyInjectionDemo { @Test @DisplayName ( "依賴注入" ) @Tag ( "test" ) void testDisplayName(TestInfo testInfo) { assertEquals( "依賴注入" , testInfo.getDisplayName()); assertEquals(Collections.singleton( "test" ), testInfo.getTags()); } } |
還有RepetitionInfoParameterResolver等內置參數解析器,將在后面介紹。
常用注解
顯示名稱
我們可以為測試類和測試方法添加自定義的名稱,這些名貴會由測試運行器和測試報告所顯示。顯示名稱沒有變量名那樣的顯示,可以是一段包含空格的長字符串,甚至還可以是Emoji表情。
1
2
3
4
5
6
7
8
9
10
11
|
@DisplayName ( "測試類可以指定顯示名稱" ) public class DisplayNameDemo { @Test @DisplayName ( "測試方法也可以指定顯示名稱" ) void testWithLongDisplayName() { } @Test @DisplayName ( "顯示名稱還可以包含表情??" ) void testWithDisplayNameWithEmoji() { } } |
禁用測試
@Disabled注解可以用到測試類或測試方法上,可以禁用對應的測試。
1
2
3
4
5
6
7
8
|
@Disabled public class DisabledTestDemo { @Test //@Disabled void testDisabled() { } } |
重復測試
如果需要讓某個測試方法運行多次,使用@RepeatedTest注解。
1
2
3
4
5
6
|
public class RepeatedTestDemo { @RepeatedTest ( 10 ) void testRepeated10Times() { } } |
還可以注入一個實例RepetitionInfo,檢查當前重復次數和總的重復次數。
1
2
3
4
5
6
7
8
9
10
11
|
public class RepeatedTestDemo { @BeforeEach void beforeEach(RepetitionInfo info) { System.out.printf( "%d - %d\n" , info.getCurrentRepetition() , info.getTotalRepetitions()); } @RepeatedTest ( 10 ) void testRepeated10Times() { } } |
附帶標簽
在前面介紹配置Gradle的時候就說了,在配置中可以選擇過濾某些標簽的測試。要在代碼中給標簽也很簡單,直接用@Tag注解即可。
1
2
3
4
5
6
7
8
9
10
11
|
@Tag ( "taggedTest" ) public class TagDemo { @Test @Tag ( "taggedTest1" ) void testWithTag1() { } @Test @Tag ( "taggedTest2" ) void testWithTag2() { } } |
嵌套測試
有時候可能需要嵌套測試來表明某些測試之間的包含關系。嵌套測試使用@Nested注解。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
@DisplayName ( "外層測試" ) public class NestedDemo { @Test void testOuter() { } @Nested @DisplayName ( "內層測試" ) class InnerTestDemo { @Test void testInner() { } } } |
需要注意只有費靜態內部類才能使用Nested注解。另外,由于Java不允許內部類有靜態方法,所以也不能有@BeforeAll和@AfterAll注解。如果想要突破這個限制,需要在嵌套內部類上添加@TestInstance(Lifecycle.PER_CLASS)注解,詳情參見Test Instance Lifecycle。
IDE支持
雖然現在JUnit 5已經出來了。但是各種工具鏈的支持還沒有跟上。目前只有Intellij IDEA和Eclipse 4.7 (Oxygen)添加了對JUnit 5的支持。所以如果在正式場合的話,使用JUnit 4還是更穩妥一點。
常見問題
區分不同版本間的@Test注解
就在我寫這篇文章的時候, 我的測試小例子就遇到了一個問題,測試通不過,顯示如下的錯誤信息。
1
2
3
4
|
Failures ( 1 ): JUnit Vintage:yitian.study.test.AssertionDemo:initializationError ClassSource [className = 'yitian.study.test.AssertionDemo' , filePosition = null ] => java.lang.Exception: Method testAssertion() should be public |
英文好的同學應該可以認出來,這個錯誤信息說的是測試方法必須是公開的。但是前面明明說了,JUnit 5取消了這個限制,那么為什么還會出現這個錯誤呢?我仔細一看,發現了錯誤所在。可能是由于以前JUnit 4用的比較多,所以IDE默認對于@Test這個注解,自動補全的是這個。
1
|
import org.junit.Test; |
這個包是JUnit 4下的@Test注解。如果我們要使用JUnit 5的話,需要的是以下這個@Test注解。
1
|
import org.junit.jupiter.api.Test; |
修改之后,再次運行測試,果然沒有問題了。當然這里為了學習和使用,我同時引用了JUnit 4的包,所以才會出現這個沖突。如果沒有什么特殊需求的話,建議只導入JUnit 5的jar包,防止出現混淆。當然都導入也可以,只不過你就需要小心區分,不要把JUnit 4的注解寫到JUnit 5的測試上。最后附上我的測試小例子,有興趣的同學可以看看。
總結
以上就是本文關于JUnit5相關內容簡介的全部內容,希望對大家有所幫助。感興趣的朋友可以繼續參閱本站其他相關專題,如有不足之處,歡迎留言指出。感謝朋友們對本站的支持!
原文鏈接:http://blog.csdn.net/u011054333/article/details/78007983