JUnit是一個Java語言的單元測試框架,。它由Kent Beck和Erich Gamma建立,,逐漸成為源于Kent Beck的sUnit的xUnit家族中最為成功的一個。JUnit有它自己的JUnit擴展生態(tài)圈,。多數Java的開發(fā)環(huán)境都已經集成了JUnit作為單元測試的工具,。
JUnit是由Erich Gamma和Kent Beck編寫的一個回歸測試框架(regression testing framework)。Junit測試是程序員測試,,即所謂白盒測試,,因為程序員知道被測試的軟件如何(How)完成功能和完成什么樣(What)的功能。Junit是一套框架,,繼承TestCase類,,就可以用Junit進行自動測試了。
安裝很簡單,,先到以下地址下載一個最新的zip包:
下載完以后解壓到你喜歡的目錄下,,假設是JUNIT_HOME,然后將JUNIT_HOME下的junit.jar包加到你的系統(tǒng)的CLASSPATH環(huán)境變量中,,對于IDE環(huán)境,,對于需要用到的junit的項目增加到lib中,其設置不同的IDE有不同的設置,,這里不多講,。
最簡單的范例如下:
1、創(chuàng)建一個TestCase的子類
package junitfaq;
import java.util.*;
import junit.framework.*;
public class SimpleTest extends TestCase {
public SimpleTest(String name) {
super(name);
}
2,、寫一個測試方法斷言期望的結果
public void testEmptyCollection(){
Collection collection = new ArrayList();
assertTrue(collection.isEmpty());
}
注意:JUnit推薦的做法是以test作為待測試的方法的開頭,,這樣這些方法可以被自動找到并被測試。
3,、寫一個suite()方法,,它會使用反射動態(tài)的創(chuàng)建一個包含所有的testXxxx方法的測試套件
public static Test suite() {
return new TestSuite(SimpleTest.class);
}
4、寫一個main()方法以文本運行器的方式方便的運行測試
public static void main(String args[]) {
junit.textui.TestRunner.run(suite());
}
}
5,、運行測試
以文本方式運行:
java junitfaq.SimpleTest
通過的測試結果是:
.
Time: 0
OK (1 tests)
Time上的小點表示測試個數,,如果測試通過則顯示OK。否則在小點的后邊標上Fail,,表示該測試失敗,。
每次的測試結果都應該是OK的,這樣才能說明測試是成功的,,如果不成功就要馬上根據提示信息進行修正了,。
如果JUnit報告了測試沒有成功,,它會區(qū)分失敗(failures)和錯誤(errors),。失敗是你的代碼中的assert方法失敗引起的,;而錯誤則是代碼異常引起的,例如ArrayIndexOutOfBoundsException,。
以圖形方式運行:
java junit.swingui.TestRunner junitfaq.SimpleTest
通過的測試結果在圖形界面的綠色條部分,。
以上是最簡單的測試樣例,在實際的測試中我們測試某個類的功能是常常需要執(zhí)行一些共同的操作,,完成以后需要銷毀所占用的資源(例如網絡連接,、數據庫連接,關閉打開的文件等),,TestCase類給我們提供了setUp方法和tearDown方法,,setUp方法的內容在測試你編寫的TestCase子類的每個testXxxx方法之前都會運行,而tearDown方法的內容在每個testXxxx方法結束以后都會執(zhí)行,。這個既共享了初始化代碼,,又消除了各個測試代碼之間可能產生的相互影響。
不要認為壓力大,,就不寫測試代碼。相反編寫測試代碼會使你的壓力逐漸減輕,,因為通過編寫測試代碼,,你對類的行為有了確切的認識。你會更快地編寫出有效率地工作代碼,。
下面是一些具體的編寫測試代碼的技巧或較好的實踐方法:
1.不要用TestCase的構造函數初始化Fixture,,而要用setUp()和tearDown()方法。
2.不要依賴或假定測試運行的順序,,因為JUnit利用Vector保存測試方法,。所以不同的平臺會按不同的順序從Vector中取出測試方法。
3.避免編寫有副作用的TestCase,。例如:如果隨后的測試依賴于某些特定的交易數據,,就不要提交交易數據。簡單的回滾就可以了,。
4.當繼承一個測試類時,,記得調用父類的setUp()和tearDown()方法。
5.將測試代碼和工作代碼放在一起,,一邊同步編譯和更新,。(使用Ant中有支持junit的task.)
6.測試類和測試方法應該有一致的命名方案。如在工作類名前加上test從而形成測試類名,。
7.確保測試與時間無關,,不要依賴使用過期的數據進行測試,。導致在隨后的維護過程中很難重現測試。
8.如果你編寫的軟件面向國際市場,,編寫測試時要考慮國際化的因素,。不要僅用母語的Locale進行測試。
9.盡可能地利用JUnit提供地assert/fail方法以及異常處理的方法,,可以使代碼更為簡潔,。
10.測試要盡可能地小,執(zhí)行速度快,。
11.不要硬性規(guī)定數據文件的路徑,。
12.利用Junit的自動異常處理書寫簡潔的測試代碼
事實上在Junit中使用try-catch來捕獲異常是沒有必要的,Junit會自動捕獲異常,。那些沒有被捕獲的異常就被當成錯誤處理,。
13.充分利用Junit 的assert/fail方法
assertSame()用來測試兩個引用是否指向同一個對象
assertEquals()用來測試兩個對象是否相等
14.確保測試代碼與時間無關
15.使用文檔生成器做測試文檔。
JUnit和ant結合
ant提供了兩個target:junit和junitreport運行所有測試用例,,并生成html格式的報表
具體操作如下:
1.將 junit.jar 放在 ANT_HOMElib 目錄下
2.修改 build.xml,,加入如下 內容:
-------------- One or more tests failed, check the report for detail... -----------------------------
運行這個target,ant會運行每個TestCase,,在report目錄下就有了很多TEST*.xml和一些網頁打開report目錄下的 index.html就可以看到很直觀的測試運行報告,,一目了然。
在Eclipse中開發(fā),、運行JUnit測試相當簡單,。因為Eclipse本身集成了JUnit相關組件,并對JUnit的運行提供了無縫的支持,。
junit3.x
我們通常使用junit 3.8
(1),、使用junit3.x版本進行單元測試時,測試類必須要繼承于TestCase父類,;
(2),、測試方法需要遵循的原則:
A、public的
B,、void的
C,、無方法參數
D、方法名稱必須以test開頭
(3),、不同的Test Case之間一定要保持完全的獨立性,,不能有任何的關聯。
(4),、我們要掌握好測試方法的順序,,不能依賴于測試方法自己的執(zhí)行順序。
demo:
public class TestMyNumber extends TestCase {
private MyNumber myNumber;
public TestMyNumber(String name) {
super(name);
}
// 在每個測試方法執(zhí)行 [之前] 都會被調用
@Override
public void setUp() throws Exception {
// System.out.println("歡迎使用Junit進行單元測試…");
myNumber = new MyNumber();
}
// 在每個測試方法執(zhí)行 [之后] 都會被調用
@Override
public void tearDown() throws Exception {
// System.out.println("Junit單元測試結束…");
}
public void testDivideByZero() {
Throwable te = null;
try {
myNumber.divide(6, 0);
Assert.fail("測試失敗");
} catch (Exception e) {
e.printStackTrace();
te = e;
}
Assert.assertEquals(Exception.class, te.getClass());
Assert.assertEquals("除數不能為 0 ", te.getMessage());
}
}
junit4.x
(1),、使用junit4.x版本進行單元測試時,,不用測試類繼承TestCase父類,,因為,junit4.x全面引入了Annotation來執(zhí)行我們編寫的測試,。
(2),、junit4.x版本,引用了注解的方式,,進行單元測試,;
(3)、junit4.x版本我們常用的注解:
A,、@Before 注解:與junit3.x中的setUp()方法功能一樣,,在每個測試方法之前執(zhí)行;
B,、@After 注解:與junit3.x中的tearDown()方法功能一樣,,在每個測試方法之后執(zhí)行;
C,、@BeforeClass 注解:在所有方法執(zhí)行之前執(zhí)行,;
D、@AfterClass 注解:在所有方法執(zhí)行之后執(zhí)行,;
E,、@Test(timeout=xxx)注解:設置當前測試方法在一定時間內運行完,否則返回錯誤,;
F,、@Test(expected=Exception.class)注解:設置被測試的方法是否有異常拋出。拋出異常類型為:Exception.class,;
G、@Ignore注解:注釋掉一個測試方法或一個類,,被注釋的方法或類,,不會被執(zhí)行。
demo:
package com.an.junit;
import static org.junit.Assert.*;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
public class TestMyNumber {
private MyNumber myNumber;
@BeforeClass
// 在所有方法執(zhí)行之前執(zhí)行
public static void globalInit() {
System.out.println("init all method...");
}
@AfterClass
// 在所有方法執(zhí)行之后執(zhí)行
public static void globalDestory() {
System.out.println("destory all method...");
}
@Before
// 在每個測試方法之前執(zhí)行
public void setUp() {
System.out.println("start setUp method");
myNumber = new MyNumber();
}
@After
// 在每個測試方法之后執(zhí)行
public void tearDown() {
System.out.println("end tearDown method");
}
@Test(timeout=600)// 設置限定測試方法的運行時間 如果超出則返回錯誤
public void testAdd() {
System.out.println("testAdd method");
int result = myNumber.add(2, 3);
assertEquals(5, result);
}
@Test
public void testSubtract() {
System.out.println("testSubtract method");
int result = myNumber.subtract(1, 2);
assertEquals(-1, result);
}
@Test
public void testMultiply() {
System.out.println("testMultiply method");
int result = myNumber.multiply(2, 3);
assertEquals(6, result);
}
@Test
public void testDivide() {
System.out.println("testDivide method");
int result = 0;
try {
result = myNumber.divide(6, 2);
} catch (Exception e) {
fail();
}
assertEquals(3, result);
}
@Test(expected = Exception.class)
public void testDivide2() throws Exception {
System.out.println("testDivide2 method");
myNumber.divide(6, 0);
fail("test Error");
}
public static void main(String[] args) {
}
}
另外junit是在極限編程和重構(refactor)中被極力推薦使用的工具,,因為在實現自動單元測試的情況下可以大大的提高開發(fā)的效率,,但是實際上編寫測試代碼也是需要耗費很多的時間和精力的,那么使用這個東西好處到底在哪里呢,?筆者認為是這樣的:
極限編程
要求在編寫代碼之前先寫測試,,這樣可以強制你在寫代碼之前好好的思考代碼(方法)的功能和邏輯,否則編寫的代碼很不穩(wěn)定,,那么你需要同時維護測試代碼和實際代碼,,這個工作量就會大大增加。因此在極限編程中,,基本過程是這樣的:構思-> 編寫測試代碼-> 編寫代碼-> 測試,,而且編寫測試和編寫代碼都是增量式的,,寫一點測一點,在編寫以后的代碼中如果發(fā)現問題可以較快的追蹤到問題的原因,,減小回歸錯誤的糾錯難度,。
重構
其好處和極限編程中是類似的,因為重構也是要求改一點測一點,,減少回歸錯誤造成的時間消耗,。
其他情況
我們在開發(fā)的時候使用junit寫一些適當的測試也是有必要的,因為一般我們也是需要編寫測試的代碼的,,可能原來不是使用的junit,,如果使用junit,而且針對接口(方法)編寫測試代碼會減少以后的維護工作,,例如以后對方法內部的修改(這個就是相當于重構的工作了),。另外就是因為junit有斷言功能,如果測試結果不通過會告訴我們哪個測試不通過,,為什么,,而如果是像以前的一般做法是寫一些測試代碼看其輸出結果,然后再由自己來判斷結果是否正確,,使用junit的好處就是這個結果是否正確的判斷是它來完成的,,我們只需要看看它告訴我們結果是否正確就可以了,在一般情況下會大大提高效率,。
JUnit是一個開放源代碼的Java測試框架,,用于編寫和運行可重復的測試。他是用于單元測試框架體系xUnit的一個實例(用于java語言),。它包括以下特性:
1,、用于測試期望結果的斷言(Assertion)
2、用于共享共同測試數據的測試工具
3,、用于方便的組織和運行測試的測試套件
4,、圖形和文本的測試運行器