JUnit4教程

工程師WWW發表於2015-06-11
因jdk5中的新特性,JUnit4也因此有了很大的改變。確切的說,Junit4簡直就不是3的擴充套件版本,而是一個全新的測試框架。下面詳細介紹JUnit4的使用方法
1.測試方法:

在junit4之前,測試類通過繼承TestCase類,並使用命名約束來定位測試,測試方法必須以“test”開頭。

Junit4中使用註釋類識別:@Test,也不必約束測試方法的名字。當然,TestCase類仍然可以工作,只不過不用這麼繁瑣而已。

Junit中還因JDK5而增加了一項新特性,靜態匯入(static import)。


2.韌體測試
所謂韌體測試(Fixture),就是測試執行執行程式(test runner)會在測試方法之前自動初始化、和回收資源的工作。JUnit4之前是通過setUpTearDown方法完成。在Junit4中,仍然可以在每個測試方法執行之前初始化欄位,和配置環境,當然也是通過註釋完成。Junit4中,通過@befroe替代setUp方法;@After替代tearDown方法。在一個測試類中,甚至可以使用多個@Before來註釋多個方法,這些方法都是在每個測試之前執行。說明一點,@Before是在每個測試方法執行前均初始化一次,同理@Ater是在每個測試方法執行完畢後,均執行一次.就是說,經這兩個註釋的初始化和登出,可以保證各個測試之間的獨立性而互不干擾,他的缺點是效率低。另外,不需要在朝類中顯示呼叫初始化和清除方法,只要他們不被覆蓋,測試執行程式將根據需要自動呼叫這些方法。超類中的@Before方法在自來的@Before方法之前呼叫(與建構函式呼叫順序一致),@After方法是子類中的在超類之前執行。
在JUnit4中加入了一項新特性。加入了兩個註釋:@BeforeClass和@AfterClass,使用這兩個註釋的方法,在該測試類中的測試方法之前、後各執行一次,而不是按照方法各執行一次。對於消耗很的啊的資源,可以使用這兩個註釋。

3.異常測試
因為使用了註釋特性,JUnit4測試異常非常的簡單和明瞭。通過對@Test傳入expected引數值,即可測試異常。通過傳入異常類後,測試類如果沒有丟擲異常或者丟擲一個不同的異常,本測試方法就將失敗。見程式碼:

/**
 * 
 */
package cn.hrmzone.junit;

import java.io.File;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.io.SAXReader;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;

/**
 * @author hrmzone.cn
 *2010-10-1
 */
public class ExceptionTest {
	File f;
	Document doc;
	@Before
	public void init() {
		f=new File("output"+File.separator+"test.xml");
	}
	@Ignore("not run")
	@Test(expected=DocumentException.class)
	public void read() throws DocumentException {
		SAXReader reader=new SAXReader();
		doc=reader.read(f);
	}
	@Test(expected=ArithmeticException.class)
	public void divideZero() {
		int n=2/0;
	}
}

在第二個測試方法中,用0做除數,將會丟擲ArithmeticException異常,所以對expected引數傳入該類。測試丟擲此異常,說明本次測試成功。如圖:
snapshot-000
測試成功後,方法前會出現一個小溝,大家可能注意到了,read()方法,程式碼中有一個奇怪的@Ignore註釋,測試後也會出現一個標識。@Ignore註釋表示忽略註釋,執行測試類時,被@Ignore註釋的方法將不會被測試,所以執行測試類後,會出行一個奇怪的標識。

4.超時測試
通過在@Test註釋中,為timeout引數指定時間值,即可進行超時測試。如果測試執行時間超過指定的毫秒數,則測試失敗。超時測試對網路連結類的非常重要,通過timeout進行超時測試,簡單異常。如下例子:

/**
 * 
 */
package cn.hrmzone.junit;

import static org.junit.Assert.assertTrue;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.junit.Before;
import org.junit.Test;

/**
 * @author hrmzone.cn
 *2010-10-1
 */
public class RegularExpressionTest {
	private String dateReg;
	private Pattern pattern;
	
	@Before 
	public void init() {
		dateReg="^\\d{4}(\\-\\d{1,2}){2}";
		pattern=Pattern.compile(dateReg);
	}
//	timeout測試是指在指定時間內就正確
	@Test(timeout=1)
	public void verifyReg() {
		Matcher matcher=pattern.matcher("2010-10-2");
		boolean isValid=matcher.matches();
//		靜態匯入功能
		assertTrue("pattern is not match",isValid);
	}
}

5.測試執行器
測試執行器:JUNit中所有的測試方法都是由它負責執行。JUnit為單元測試提供了預設的測試執行器,但是沒有限制必須使用預設的執行器。自己定製的測試執行器必須繼承自org.junit.runner.Runner。而且還可以為每一個測試類指定某個執行器:@Runwith(CustomTestRunner.class).在JUnit中,有兩個高階特性會需要自定義執行器。

6.測試套件
JUnit4中最顯著的特性是沒有套件(套件機制用於將測試從邏輯上的分組並將這這些測試作為一個單元測試來執行)。為了替代老版本的套件測試,套件被兩個新註釋代替:@RunWith、@SuteClasses。通過@RunWith指定一個特殊的執行器:Suite.class套件執行器,並通過@SuiteClasses註釋,將需要進行測試的類列表作為引數傳入。
使用方法為:
a.建立一個空類作為測試套件的入口
b.將@RunWith、@SuiteClasses註釋修飾這個空類;
c.吧Suite.class作為引數傳入@RunWith註釋,以提示JUnit將此類指定為執行器;
d.將需要測試的類組成陣列作為@SuiteClasses的引數。
注意:這個空類必須使用public修飾符,而且存在public的無參建構函式(類的預設建構函式即可)。測試程式碼如下:將先前的兩個測試類:RegularExpressionTest,Exception作為一個測試套件進行測試:

/**
 * 
 */
package cn.hrmzone.junit;

import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;

/**
 * @author hrmzone.cn
 *2010-10-2
 */
@RunWith(Suite.class)
@SuiteClasses({RegularExpressionTest.class,ExceptionTest.class})
public class SuiteTest {

}

@SuiteClasses引數為需要測試類的陣列,可以使用{},將兩個測試類傳入。
測試套件類為空類,需要一個無參的public建構函式,使用預設建構函式即可。
執行結果如圖,可以帶到作為引數的兩個測試類均執行,與單獨測試一致。圖2
snapshot-001
7.引數化測試
為測試程式健壯性,可能需要模擬不同的引數對方法進行測試,如果在為每一個型別的引數建立一個測試方法,呵呵,人都瘋掉了。幸好有引數化測試出現了。它能夠建立由引數值供給的通用測試,從而為每個引數都執行一次,而不必要建立多個測試方法。注:測試方法(@Test註釋的方法)是不能有引數的。
引數化測試編寫流程如下:
a.為引數化測試類用@RunWith註釋指定特殊的執行器:Parameterized.class;
b.在測試類中宣告幾個變數,分別用於儲存期望值和測試用的資料,並建立一個使用者幾個引數的建構函式;
c.建立一個靜態(static)測試資料供給(feed)方法,其返回型別為Collection,並用@Parameter註釋以修飾;
d.編寫測試方法(用@Test註釋)。
測試示例程式碼如下:

/**
 * 
 */
package cn.hrmzone.junit;

import static org.junit.Assert.assertEquals;

import java.util.Arrays;
import java.util.Collection;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

/**
 * @author hrmzone.cn
 *2010-10-2
 */
@RunWith(Parameterized.class)
public class ParameterTest {
	private String dateReg;
	private Pattern pattern;
//	資料成員變數
	private String phrase;
	private boolean match;
	
//	使用資料的建構函式
	public ParameterTest(String phrase, boolean match) {
		super();
		this.phrase = phrase;
		this.match = match;
	}


	@Before 
	public void init() {
		dateReg="^\\d{4}(\\-\\d{1,2}){2}";
		pattern=Pattern.compile(dateReg);
	}
	
//	測試方法
	@Test
	public void verifyDate() {
		Matcher matcher=pattern.matcher(phrase);
		boolean isValid=matcher.matches();
		assertEquals("Pattern don't validate the data format",isValid,match);
	}
//	資料供給方法(靜態,用@Parameter註釋,返回型別為Collection
	@Parameters
	public static Collection dateFeed() {
		return Arrays.asList(new Object[][] {
				{"2010-1-2",true},
				{"2010-10-2",true},
				{"2010-123-1",false},
				{"2010-12-45",false}
		});
	}
}

執行結果如圖:
snapshot-002
因為程式碼只測試日期格式,並未測試日期範圍的正確性,2010-12-45這數值測試與期望值不符。

8.陣列斷言
JUnit4中新增了一個用於比較陣列的新斷言(Assert),這樣不必使用迭代比較陣列中的條目。
public static void assertEquals(Object[] expected, Object[] actual)
public static void assertEquals(String message, Object[] expected,Object[] actual)

JUnit4是向前相容的,在JUnit4中依然可以使用JUnit3的測試程式碼,而不需要做任何改動。
JUnit測試框架將測試更加便捷和容易,編寫測試程式碼也是簡單、明瞭,但功能依然強大。