單元測試更多的是在開發階段完成,開發人員每寫一個函數的時候都會寫相應的單元測試。對於java代碼,廣泛使用的是jUnit,根據jUnit能夠本身相應的開發一套自動化測試框架。這個的前提是要學會junit,先知道怎麼用,才能知道怎麼爲我所用。java
學習JUnit的操做很簡單,JUnit是一個敏捷編程的開發框架,他的設計很值得學習。這也是我學習JUnit的緣由。sql
JUnit最大的特色就是:各個方法之間是獨立的,一個方法的失敗不會影響另外一個方法的執行。數據庫
JUnit 3:是基於反射機制的方法,有命名的約束,必須以test開頭。每個類都繼承了TestCase類,而TestCase的父類是Assert類。即全部測試類都是TestCase類的子類。編程
JUnit 4:引入了註解(annotation),經過解析註解就能夠爲測試提供相應的信息。@Test代表這是要執行的測試方法,無論測試方法的名字是什麼,都會執行。每個類能夠不繼承任何類,能夠是一個普通類也能夠去繼承一個類或實現一個接口,要實現測試只須要在要測試的方法以前加@Test註釋就能夠了,可是仍然能夠直接使用斷言,靜態導入import static org.junit.Assert.*;數組
爲了更好地理解junit測試用例,我找了一個簡單的例子來練習:框架
例若有這麼一個類Greeting,根據天天不一樣的時間來返回不一樣的問候語:函數
package com.test.one; public class Greeting { public static final String GREETING_MORNING = "Good Morning Sunshine!"; public static final String GREETING_AFTERNOON = "Just a few more hours before quiting time!"; public static final String GREETING_EVENING = "I am outta here"; public static final int MAX_HOUR_MORNING = 12; public static final int MAX_HOUR_AFTERNOON = 17; private java.sql.Timestamp iGreetingTime = null; public Greeting(){ this(System.currentTimeMillis()); } public Greeting(long greetingTime) { super(); iGreetingTime = new java.sql.Timestamp(greetingTime); } public String getGreeting(){ if(iGreetingTime.getHours()<MAX_HOUR_MORNING){ return GREETING_MORNING; }else if (iGreetingTime.getHours()<MAX_HOUR_AFTERNOON) { return GREETING_AFTERNOON; }else { return GREETING_EVENING; } } }
咱們須要測試這個類,看是否能夠根據時間的不一樣,來呈現不一樣的問候語。建立junit的測試用例須要遵循一下幾點:單元測試
一、Junit類需繼承TestCase學習
二、setUp()和setDown()爲每一個方法準備或銷燬測試裝備測試
三、建立public的方法,方法名爲test開頭,如testMorningGreeting(),使用assert判斷實際返回的值和指望值
四、super(testMethod)爲每一個測試方法生成實例。首先執行setUp(),而後執行testMethod ,最後執行tearDown()。
package com.test.test; import java.sql.Time; import com.test.one.Greeting; import junit.framework.TestCase; public class GreetingTest extends TestCase { protected void setUp() throws Exception { super.setUp(); } protected void tearDown() throws Exception{ super.tearDown(); } public void testMorningGreeting() throws Exception { Time time = new Time(9, 0, 0); Greeting greeting = new Greeting(time.getTime()); assertEquals("the morning greeting expected", Greeting.GREETING_MORNING, greeting.getGreeting()); } public void testAfternoonGreeting() throws Exception { Time time = new Time(12, 0, 0); Greeting greeting = new Greeting(time.getTime()); assertEquals("the Afternoon greeting expected", Greeting.GREETING_AFTERNOON, greeting.getGreeting()); } public void testEveningGreeting() throws Exception { Time time = new Time(18, 0, 0); Greeting greeting = new Greeting(time.getTime()); assertEquals("the Evening greeting expected", Greeting.GREETING_EVENING, greeting.getGreeting()); } public GreetingTest(String testMethod){ super(testMethod); } }
這個只是單個測試用例,junit也提供了測試套件組織想要執行的測試用例。
若是你已經有了兩個測試用例,AddJobCmdImpTest、RemoveJobCmdImpTest等TestCase的子類別,若是想一次運行這兩個測試,須要使用AllTest的類,表明一個測試套件。
public class AllTest { public static Test suite(){ TestSuite suite = new TestSuite("Test for XXXX"); suite.addTest(new TestSuite(AddJobCmdImpTest.class)); suite.addTest(new TestSuite(RemoveJobCmdImpTest.class)); return suite; } }
如下是JUnit4的代碼:
public class GreetingTest extends TestCase { @Before public void setUp() throws Exception { super.setUp(); } @After public void tearDown() throws Exception{ super.tearDown(); } @Test public void ttrestMorningGreeting() throws Exception { Time time = new Time(9, 0, 0); Greeting greeting = new Greeting(time.getTime()); assertEquals("the morning greeting expected", Greeting.GREETING_MORNING, greeting.getGreeting()); } @Test public void testAfternoonGreeting() throws Exception { Time time = new Time(12, 0, 0); Greeting greeting = new Greeting(time.getTime()); assertEquals("the Afternoon greeting expected", Greeting.GREETING_AFTERNOON, greeting.getGreeting()); } @Test public void testEveningGreeting() throws Exception { Time time = new Time(18, 0, 0); Greeting greeting = new Greeting(time.getTime()); assertEquals("the Evening greeting expected", Greeting.GREETING_EVENING, greeting.getGreeting()); } }
關於JUnit4的一些知識點:
1)、註釋
註釋 | 說明 |
@Before | 用於方法註釋,表示該方法在每一個測試方法執行前執行一次,可用於一些初始工做 |
@BeforeClass | 用於方法註釋,該方法在全部測試方法運行前運行,且只運行一次,添加該註釋的方法必須修飾爲 public static void 且沒有參數。 |
@Test | 方法註釋,表示測試方法。該方法有兩個屬性 a: expected :該屬性表示測試方法必須拋出一個異常,且異常的類型必須是該屬性要求的類型,不然表示測試方法失敗。也叫作異常測試。 例如:@Test(expected=IndexOutOfBoundsException.class) b:timeout 用於超時測試,表示該測試方法的執行時間若是超過了要求的時間則失敗 單位爲毫秒 例如:@Test(timeout=100) |
@Ignore | 方法註釋,表示會被忽略的測試方法 |
@After | 方法註釋,被註釋的方法會在每一個測試方法執行完成以後執行一次,若是其它的方法拋出了異常,該方法一樣會被執行。主要用於釋放在@Before方法中初始化的資源。 |
@AfterClass | 方法註釋,功能同@After,只不過是該方法釋放的是@BeforeClass方法 初始化的資源。且在全部的測試方法執行完成以後,只執行一次。 |
一個JUnit 4 的單元測試用例執行順序爲:
@BeforeClass –> @Before –> @Test –> @After –> @AfterClass
2)、failure 和error
failure:是因爲預期的結果和實際運行結果不一樣而致使的,例如當使用assertEqual()或其它assertXXX()方法斷言失敗時,就會報出failure,若是發現failure,你就要去檢查你的測試方法或者是被測試方法中的填寫的邏輯是否有誤。簡單點,就是預想到的。
error:是填寫程序是沒有考慮到的問題,在執行測試的斷言以前,程序就由於某種類型的意外而中止,好比說咱們在操做數組的時候,由於存取超出索引就會引起ArrayIndexOutOfBoundsException,這時程序就會報error,程序將沒法運行下去,提早結束,這個時候就要檢查被測試方法中是否由欠缺考慮的地方。簡單點,就是預想不到的,沒有執行到斷言。
JUnit4是JUnit框架有史以來的最大改進,其主要目標即是利用Java5的Annotation特性簡化測試用例的編寫。Annotation翻譯成元數據。元數據是什麼?元數據就是描述數據的數據。也就是說,這個東西在Java裏面能夠用來和public、static等關鍵字同樣來修飾類名、方法名、變量名。修飾的做用描述這個數據是作什麼用的,差很少和public描述這個數據是公有的同樣。
@Test(expected=*.class)
在JUnit4.0以前,對錯誤的測試,咱們只能經過fail來產生一個錯誤,並在try塊裏面assertTrue(true)來測試。如今,經過@Test元數據中的expected屬性。expected屬性的值是一個異常的類型
@Test(timeout=xxx):
該元數據傳入了一個時間(毫秒)給測試方法,
若是測試方法在制定的時間以內沒有運行完,則測試也失敗。
@ignore:
該元數據標記的測試方法在測試中會被忽略。當測試的方法尚未實現,或者測試的方法已通過時,或者在某種條件下才能測試該方法(好比須要一個數據庫聯接,而在本地測試的時候,數據庫並無鏈接),那麼使用該標籤來標示這個方法。同時,你能夠爲該標籤傳遞一個String的參數,來代表爲何會忽略這個測試方法。好比:@lgnore(「該方法尚未實現」),在執行的時候,僅會報告該方法沒有實現,而不會運行測試方法。
@RunWith(Parameterized.class) 參數化測試。
你可能遇到過這樣的函數,它的參數有許多特殊值,或者說他的參數分爲不少個區域。好比,一個對考試分數進行評價的函數,返回值分別爲「優秀,良好,通常,及格,不及格」,所以你在編寫測試的時候,至少要寫5個測試,把這5中狀況都包含了,這確實是一件很麻煩的事情。只寫一個測試函數,把這若干種狀況做爲參數傳遞進去,一次性的完成測試。
@RunWith(Parameterized.class)
public class SquareTest ...{
private static Calculator calculator = new Calculator();
private int param;
private int result;
@Parameters
public static Collection data() ...{
return Arrays.asList(new Object[][]...{
...{2, 4},
...{0, 0},
...{-3, 9},
});
}
//構造函數,對變量進行初始化
public SquareTest(int param, int result) ...{
this.param = param;
this.result = result;
}
@Test
public void square() ...{
calculator.square(param);
assertEquals(result, calculator.getResult());
}
}
下面咱們對上述代碼進行分析。首先,你要爲這種測試專門生成一個新的類,而不能與其餘測試共用同一個類,此例中咱們定義了一個SquareTest類。而後,你要爲這個類指定一個Runner,而不能使用默認的Runner了,由於特殊的功能要用特殊的Runner嘛。@RunWith(Parameterized.class)這條語句就是爲這個類指定了一個ParameterizedRunner。第二步,定義一個待測試的類,而且定義兩個變量,一個用於存放參數,一個用於存放期待的結果。接下來,定義測試數據的集合,也就是上述的data()方法,該方法能夠任意命名,可是必須使用@Parameters標註進行修飾。這個方法的框架就不予解釋了,你們只須要注意其中的數據,是一個二維數組,數據兩兩一組,每組中的這兩個數據,一個是參數,一個是你預期的結果。好比咱們的第一組{2, 4},2就是參數,4就是預期的結果。這兩個數據的順序無所謂,誰前誰後均可以。以後是構造函數,其功能就是對先前定義的兩個參數進行初始化。在這裏你可要注意一下參數的順序了,要和上面的數據集合的順序保持一致。若是前面的順序是{參數,期待的結果},那麼你構造函數的順序也要是「構造函數(參數, 期待的結果)」,反之亦然。
打包測試
在一個項目中,只寫一個測試類是不可能的,咱們會寫出不少不少個測試類。但是這些測試類必須一個一個的執行,也是比較麻煩的事情。鑑於此,JUnit提供了打包測試的功能,將全部須要運行的測試類集中起來,一次性的運行完畢,大大的方便了咱們的測試工做。具體代碼以下:
@RunWith(Suite.class)
@Suite.SuiteClasses(...{CalculatorTest.class, SquareTest.class})
public class AllCalculatorTests ...{}
你們能夠看到,這個功能也須要使用一個特殊的Runner,所以咱們須要向@RunWith標註傳遞一個參數Suite.class。同時,咱們還須要另一個標註@Suite.SuiteClasses,來代表這個類是一個打包測試類。咱們把須要打包的類做爲參數傳遞給該標註就能夠了。有了這兩個標註以後,就已經完整的表達了全部的含義,所以下面的類已經可有可無,隨便起一個類名,內容所有爲空既可。