Junit單元測試框架是Java程序開發(fā)必備的測試利器,現(xiàn)在最常用的就是Junit4了,在Junit4中所有的測試用例都使用了注解的形式,這比Junit3更加靈活與方便。之前在公司的關(guān)于單元測試的培訓(xùn)課程中,講師僅僅講述了Junit4的基本的與生命周期相關(guān)的注解的使用,主要包括@BeforeClass、@Before、@Test、@After、@AfterClass這些注解,這些在應(yīng)付普通簡單的單元測試已經(jīng)足夠,然而有很多更加復(fù)雜且也會經(jīng)常遇到的測試需求依靠這些生命周期注解并不能完成!因此這篇分享將為您呈現(xiàn)Junit4的另一片新大陸,且看陳述…
其實,在單元測試培訓(xùn)課程中,講師并沒有講到Junit4的核心,例如為什么Junit沒有main()方法就能運行(因為我們知道無論是什么程序都必須得有一個程序入口,而它通常是main);在例如Junit的核心組成部分是什么?如何更改Junit在運行單元測試時獲取數(shù)據(jù)和執(zhí)行測試的行為?更具體一點,如果我要為一個需要兩個參數(shù)的方法進行測試,如何使用我所提供的參數(shù)的所有排列組合對方法進行測試?如果我需要在茫茫的測試用例中只測試與特定類相關(guān)的用例該怎么做…….
在這之前,先糾正一點------Junit4可以直接運行我們的某個方法,沒有main入口函數(shù)是斷然不行的。正如我之前給我們組的一個妹子講Spring的時候告訴她,在測試方法中,對測試方法所在的類添加Spring的 (Compent注解或者為該類的成員變量添加)Resource注解并沒有什么卵用,即Spring根本不會來掃描這個測試類,更不會為這個類注入屬性值。為什么這么說呢?因為Spring是在測試類中由被@Before標注的方法所啟動的,這時候,JVM已經(jīng)將此測試類實例化了,而這并不是由Spring實例化的,Spring晚了一步,所以在Spring的容器中并沒有此類的實例。那么Junit4真的有main方法嗎?沒錯,既然它能直接運行我們的方法,那它必然自己為JVM提供了程序入口。其實在org.junit.runner包下,有個JUnitCore.class,其中就有一個 標準的main方法,這就是JUnit入口函數(shù)。如此看來,它其實和我們直接在自己的main方法中跑我們要測試的方法在本質(zhì)上是一樣的。
接下來,我要說的就是Junit測試框架的新大陸,或者說是其核心組件,也正是講師所沒有講到但卻十分有用的東西------Runner,即Junit的運行器!
Runner只是一個抽象類,表示用于運行Junit測試用例的工具,通過它可以運行測試并通知Notifier運行的結(jié)果。通常我們可以在待測方法所在的類之上使用@RunWith注解來為這個測試類指定一個特定的Runner。不過在很多情況下,我們并沒有這么做,那是因為,我們使用了Junit的默認Runnner------BlockJunit4ClassRunner。當我們不為測試類添加@RunWith注解的時候,其實使用的就是這個Runner,它作為默認Runner只為我們提供了基本的基于Junit生命周期的測試注解。而有更多非常規(guī)的測試需求,則需要我們?yōu)闇y試類添加@RunWith注解并指定特定的Runner來完成!下面列出一些比較有用的Runner。
一、Suit------它可以一次生執(zhí)行全面在多個類中的測試用例,例如:
1
2
3
4
5
|
@RunWith (Suite. class ) @SuiteClasses ({Person. class , People. class }) public class TestSuitMain{ //雖然這個類是空的,但依然可以運行Junit測試,運行時,它會將Person.class和//People.class中的所有測試用命都執(zhí)行一遍! } |
二、Parameterized------在普通的單元測試中被@Test注解標注的測試方法只能是public void的,且不能有任何輸入?yún)?shù)。而這時常會給我們造成困擾,因為有時候我們需要為測試方法輸入?yún)?shù),甚至是批量指定多個待測參數(shù)。這時Parameterized這個Runner就能滿足我們的要求,用法如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
@RunWith (Parameterized. class ) public class TestGenerateParams{ private String greeting; public TestGenerateParams(String greeting){ super (); this .greeting = greeting; } @Test public void testParams(){ System.out.println(greeting); } /** * 這里的返回的應(yīng)該是一個可迭代數(shù)組,且方法必須是public static * @return */ @Parameters public static List getParams(){ return Arrays.asList( new String[][]{{ "hello" },{ "hi" },{ "good morning" },{ "how are you" }}); } } |
三、Category------繼承自Suit,更強大,它可以讓我們對測試類中被測試的方法進行分類執(zhí)行,例如Person對象具有一些屬性,這些屬性擁有g(shù)et/set方法,同時還有一些普通方法。我們可以將獲取屬性的get方法和普通方法進行分類測試。如:
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
31
32
|
public class PersonTest{ @Category (AttributeFun. class ) @Test public void testGetAge(){ int age = person.getAge(); assertEquals( 3 , age); } @Category (AttributeFun. class ) @Test public void testGetName(){ String name = person.getName(); assertEquals( "Willard" , name); } @Category (BehaviorFun. class ) @Test public void testTalk(){ String message = person.talkTo( "Jimy" ); assertNotNull(message); } @Category (BehaviorFun. class ) @Test (timeout= 200 ) public void testWalk(){ person.walk(); } } //對應(yīng)的測試執(zhí)行代碼如下: @RunWith (Categories. class ) @SuiteClasses (PersonTest. class ) @IncludeCategory (AttributeFun. class ) public class CategoryTest{ //注意,如果不加@IncludeCategory注解,那么就和使用Suit具有一樣的效果了。 } |
四、Theories------雖意為原理或推測的意思,但我在這里以更直觀的方式表述它:提供一組參數(shù)的排列組合值作為待沒方法的輸入?yún)?shù)。同時注意到在使用Theories這個Runner的時候,我們的待測方法可以擁有輸入?yún)?shù),而這在其它的Runner中的測試方法是不成的。下面是一個例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
@RunWith (Theories. class ) public class TheoriesTest{ @DataPoint public static String nameValue1 = "Tony" ; @DataPoint public static String nameValue2 = "Jim" ; @DataPoint public static int ageValue1 = 10 ; @DataPoint public static int ageValue2 = 20 ; @Theory public void testMethod(String name, int age){ System.out.println(String.format( "%s's age is %s" , name, age)); } } |
上面的代碼的意思是,將”Tony”、”Jim”、10、20四個參數(shù)以類型合法的排列組合傳給待沒方法。因此輸出的結(jié)果必然也有2x2=4種:
1
2
3
4
|
Tony's age is 10 Tony's age is 20 Jim's age is 10 Jim's age is 20 |
不過,為了簡單,我們除了可以使用@DataPoint注解來提供參數(shù)之外,還可以通過@DataPoints注解來提供參數(shù),參照上述代碼,只需要將@DataPoint注解標注的四個字段參數(shù)替換為如下的兩個即可:
1
2
3
4
|
@DataPoints public static String[] names = { "Tony" , "Jim" }; @DataPoints public static int [] ageValue1 = { 10 , 20 }; |
上展示了四個Junit運行器的使用示例,有這個四個運行器的支持,基本上大部分的測試需求得可以得到解決。當然Junit提供的功能遠不止這些。除此之外,我們還可以使用Junit4提供的Rule/Assume/Assert等。
其中使用Rule可以為單元測試指定測試規(guī)則,下面展示了這些可用的Rule:
Verifier: 驗證測試執(zhí)行結(jié)果的正確性。
ErrorCollector: 收集測試方法中出現(xiàn)的錯誤信息,測試不會中斷,如果有錯誤發(fā)生測試結(jié)束后會標記失敗。
ExpectedException: 提供靈活的異常驗證功能。
Timeout: 用于測試超時的Rule。
ExternalResource: 外部資源管理。
TemporaryFolder: 在JUnit的測試執(zhí)行前后,創(chuàng)建和刪除新的臨時目錄。
TestWatcher: 監(jiān)視測試方法生命周期的各個階段。
TestName: 在測試方法執(zhí)行過程中提供獲取測試名字的能力。
此外,Assume表示假設(shè),但它實際是對待沒方法的參數(shù)進行合法性校驗的,如果校驗不合格則直接拋異常,而不執(zhí)行測試。這和Guava中的Predictions類似。Assume提供的校驗規(guī)則如下:
assumeTrue/assumeFalse、 assumeNotNull、 assumeThat、 assumeNoException
例如:(通過下述代碼也可以看到,要使用參數(shù),則應(yīng)使用@Theory注解)
1
2
3
4
5
|
@Theory public void printAge(String name, int age){ Assume.assumeTrue(age > 0 ); //如果參數(shù)age<=0,會拋AssumptionViolatedException異常 System.out.println(String.format( "%s's Name is %s." , name, age)); } |
Assert是Junit提供的斷言,與Assume不同,Assert是對測試結(jié)果的校驗,它提供的檢驗規(guī)則如下:
AssertTrue、AssertFalse:結(jié)果的true、false。
AssertThat:使用Matcher做自定義的校驗。
AssertEquals、AssertNotEquals:判斷兩個對象是否相等。
AssertNull、AssertNotNull:判斷對象是否為空。
AssertSame:判斷兩個對象是否為同一個,不同于equals這里是使用“==”判斷。
AssertArrayEquals:判斷兩個數(shù)組是否相等。
總結(jié)
以上就是本文關(guān)于junit4單元測試高級用法的全部內(nèi)容,希望對大家有所幫助。希望大家對本站多多支持!
原文鏈接:http://www.cnblogs.com/yepei/p/5649352.html