Junit單元測試框架是Java程序開發必備的測試利器,如今最經常使用的就是Junit4了,在Junit4中全部的測試用例都使用了註解的形式,這比Junit3更加靈活與方便。以前在公司的關於單元測試的培訓課程中,講師僅僅講述了Junit4的基本的與生命週期相關的註解的使用,主要包括@BeforeClass、@Before、@Test、@After、@AfterClass這些註解,這些在應付普通簡單的單元測試已經足夠,然而有不少更加複雜且也會常常遇到的測試需求依靠這些生命週期註解並不能完成!所以這篇分享將爲您呈現Junit4的另外一片新大陸,且看陳述… 數組
其實,在單元測試培訓課程中,講師並無講到Junit4的核心,例如爲何Junit沒有main()方法就能運行(由於咱們知道不管是什麼程序都必須得有一個程序入口,而它一般是main);在例如Junit的核心組成部分是什麼?如何更改Junit在運行單元測試時獲取數據和執行測試的行爲?更具體一點,若是我要爲一個須要兩個參數的方法進行測試,如何使用我所提供的參數的全部排列組合對方法進行測試?若是我須要在茫茫的測試用例中只測試與特定類相關的用例該怎麼作……. 框架
在這以前,先糾正一點------Junit4能夠直接運行咱們的某個方法,沒有main入口函數是斷然不行的。正如我以前給咱們組的一個妹子講Spring的時候告訴她,在測試方法中,對測試方法所在的類添加Spring的 (Compent註解或者爲該類的成員變量添加)Resource註解並無什麼卵用,即Spring根本不會來掃描這個測試類,更不會爲這個類注入屬性值。爲何這麼說呢?由於Spring是在測試類中由被@Before標註的方法所啓動的,這時候,JVM已經將此測試類實例化了,而這並非由Spring實例化的,Spring晚了一步,因此在Spring的容器中並無此類的實例。那麼Junit4真的有main方法嗎?沒錯,既然它能直接運行咱們的方法,那它必然本身爲JVM提供了程序入口。其實在org.junit.runner包下,有個JUnitCore.class,其中就有一個 標準的main方法,這就是JUnit入口函數。如此看來,它其實和咱們直接在本身的main方法中跑咱們要測試的方法在本質上是同樣的。 函數
接下來,我要說的就是Junit測試框架的新大陸,或者說是其核心組件,也正是講師所沒有講到但卻十分有用的東西------Runner,即Junit的運行器! 工具
Runner只是一個抽象類,表示用於運行Junit測試用例的工具,經過它能夠運行測試並通知Notifier運行的結果。一般咱們能夠在待測方法所在的類之上使用@RunWith註解來爲這個測試類指定一個特定的Runner。不過在不少狀況下,咱們並無這麼作,那是由於,咱們使用了Junit的默認Runnner------BlockJunit4ClassRunner。當咱們不爲測試類添加@RunWith註解的時候,其實使用的就是這個Runner,它做爲默認Runner只爲咱們提供了基本的基於Junit生命週期的測試註解。而有更多很是規的測試需求,則須要咱們爲測試類添加@RunWith註解並指定特定的Runner來完成!下面列出一些比較有用的Runner。 單元測試
1、Suit------它能夠一次生執行全面在多個類中的測試用例,例如: 測試
@RunWith(Suite.class) @SuiteClasses({Person.class, People.class}) public class TestSuitMain{ //雖然這個類是空的,但依然能夠運行Junit測試,運行時,它會將Person.class和//People.class中的全部測試用命都執行一遍! }
2、Parameterized------在普通的單元測試中被@Test註解標註的測試方法只能是public void的,且不能有任何輸入參數。而這時常會給咱們形成困擾,由於有時候咱們須要爲測試方法輸入參數,甚至是批量指定多個待測參數。這時Parameterized這個Runner就能知足咱們的要求,用法以下: ui
@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); } /** * 這裏的返回的應該是一個可迭代數組,且方法必須是public static * @return */ @Parameters public static List getParams(){ return Arrays.asList(new String[][]{{"hello"},{"hi"},{"good morning"},{"how are you"}}); } }
3、Category------繼承自Suit,更強大,它可讓咱們對測試類中被測試的方法進行分類執行,例如Person對象具備一些屬性,這些屬性擁有get/set方法,同時還有一些普通方法。咱們能夠將獲取屬性的get方法和普通方法進行分類測試。如: this
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(); } } //對應的測試執行代碼以下: @RunWith(Categories.class) @SuiteClasses(PersonTest.class) @IncludeCategory(AttributeFun.class) public class CategoryTest{ //注意,若是不加@IncludeCategory註解,那麼就和使用Suit具備同樣的效果了。 }
4、Theories------雖意爲原理或推測的意思,但我在這裏以更直觀的方式表述它:提供一組參數的排列組合值做爲待沒方法的輸入參數。同時注意到在使用Theories這個Runner的時候,咱們的待測方法能夠擁有輸入參數,而這在其它的Runner中的測試方法是不成的。下面是一個例子: spa
@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」、十、20四個參數以類型合法的排列組合傳給待沒方法。所以輸出的結果必然也有2x2=4種: orm
Tony's age is 10
Tony's age is 20
Jim's age is 10
Jim's age is 20
不過,爲了簡單,咱們除了可使用@DataPoint註解來提供參數以外,還能夠經過@DataPoints註解來提供參數,參照上述代碼,只須要將@DataPoint註解標註的四個字段參數替換爲以下的兩個便可:
@DataPoints public static String[] names = {"Tony", "Jim"}; @DataPoints public static int[] ageValue1 = {10, 20};
上展現了四個Junit運行器的使用示例,有這個四個運行器的支持,基本上大部分的測試需求得能夠獲得解決。固然Junit提供的功能遠不止這些。除此以外,咱們還可使用Junit4提供的Rule/Assume/Assert等。
其中使用Rule能夠爲單元測試指定測試規則,下面展現了這些可用的Rule:
Verifier: 驗證測試執行結果的正確性。
ErrorCollector: 收集測試方法中出現的錯誤信息,測試不會中斷,若是有錯誤發生測試結束後會標記失敗。
ExpectedException: 提供靈活的異常驗證功能。
Timeout: 用於測試超時的Rule。
ExternalResource: 外部資源管理。
TemporaryFolder: 在JUnit的測試執行先後,建立和刪除新的臨時目錄。
TestWatcher: 監視測試方法生命週期的各個階段。
TestName: 在測試方法執行過程當中提供獲取測試名字的能力。
此外,Assume表示假設,但它實際是對待沒方法的參數進行合法性校驗的,若是校驗不合格則直接拋異常,而不執行測試。這和Guava中的Predictions相似。Assume提供的校驗規則以下:
assumeTrue/assumeFalse、 assumeNotNull、 assumeThat、 assumeNoException
例如:(經過下述代碼也能夠看到,要使用參數,則應使用@Theory註解)
@Theory public void printAge(String name, int age){ Assume.assumeTrue(age > 0);//若是參數age<=0,會拋AssumptionViolatedException異常 System.out.println(String.format("%s's Name is %s.", name, age)); }
Assert是Junit提供的斷言,與Assume不一樣,Assert是對測試結果的校驗,它提供的檢驗規則以下:
AssertTrue、AssertFalse:結果的true、false。
AssertThat:使用Matcher作自定義的校驗。
AssertEquals、AssertNotEquals:判斷兩個對象是否相等。
AssertNull、AssertNotNull:判斷對象是否爲空。
AssertSame:判斷兩個對象是否爲同一個,不一樣於equals這裏是使用「==」判斷。
AssertArrayEquals:判斷兩個數組是否相等。