java单元测试总结
生活随笔
收集整理的這篇文章主要介紹了
java单元测试总结
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
java單元測試(使用junit)
http://www.cnblogs.com/feiweiwei/archive/2009/06/16/1024623.html? ? JUnit是由 Erich Gamma 和 Kent Beck 編寫的一個回歸測試框架(regression testing framework),供Java開發人員編寫單元測試之用。?
1、概述?
Junit測試是程序員測試,即所謂白盒測試,因為程序員知道被測試的軟件如何(How)完成功能和完成什么樣(What)的功能。?
Junit本質上是一套框架,即開發者制定了一套條條框框,遵循這此條條框框要求編寫測試代碼,如繼承某個類,實現某個接口,就可以用Junit進行自動測試了。?
由于Junit相對獨立于所編寫的代碼,可以測試代碼的編寫可以先于實現代碼的編寫,XP 中推崇的 test first design的實現有了現成的手段:用Junit寫測試代碼,寫實現代碼,運行測試,測試失敗,修改實現代碼,再運行測試,直到測試成功。以后對代碼的修改和優化,運行測試成功,則修改成功。?
Java 下的 team 開發,采用 cvs(版本控制) + ant(項目管理) + junit(集成測試) 的模式時,通過對ant的配置,可以很簡單地實現測試自動化。?
對不同性質的被測對象,如Class,Jsp,Servlet,Ejb等,Junit有不同的使用技巧,以后慢慢地分別講敘。以下以Class測試為例講解,除非特殊說明。?
2、下載安裝?
去Junit主頁下載最新版本3.8.1程序包junit-3.8.1.zip?
用winzip或unzip將junit-3.8.1.zip解壓縮到某一目錄名為$JUNITHOME?
將junit.jar和$JUNITHOME/junit加入到CLASSPATH中,加入后者只因為測試例程在那個目錄下。?
注意不要將junit.jar放在jdk的extension目錄下?
運行命令,結果如右圖。?
java junit.swingui.TestRunner junit.samples.AllTests?
3、Junit架構?
下面以Money這個類為例進行說明。?
public class Money {?
private int fAmount;//余額?
private String fCurrency;//貨幣類型?
public Money(int amount, String currency) {?
fAmount= amount;?
fCurrency= currency;?
}?
public int amount() {?
return fAmount;?
}?
public String currency() {?
return fCurrency;?
}?
public Money add(Money m) {//加錢?
return new Money(amount()+m.amount(), currency());?
}?
public boolean equals(Object anObject) {//判斷錢數是否相等?
if (anObject instanceof Money) {?
Money aMoney= (Money)anObject;?
return aMoney.currency().equals(currency())?
&& amount() == aMoney.amount();?
}?
return false;?
}?
}?
Junit本身是圍繞著兩個設計模式來設計的:命令模式和集成模式.?
命令模式?
利用TestCase定義一個子類,在這個子類中生成一個被測試的對象,編寫代碼檢測某個方法被調用后對象的狀態與預期的狀態是否一致,進而斷言程序代碼有沒有bug。?
當這個子類要測試不只一個方法的實現代碼時,可以先建立測試基礎,讓這些測試在同一個基礎上運行,一方面可以減少每個測試的初始化,而且可以測試這些不同方法之間的聯系。?
例如,我們要測試Money的Add方法,可以如下:?
public class MoneyTest extends TestCase { //TestCase的子類?
public void testAdd() { //把測試代碼放在testAdd中?
Money m12CHF= new Money(12, "CHF"); //本行和下一行進行一些初始化?
Money m14CHF= new Money(14, "CHF");?
Money expected= new Money(26, "CHF");//預期的結果?
Money result= m12CHF.add(m14CHF); //運行被測試的方法?
Assert.assertTrue(expected.equals(result)); //判斷運行結果是否與預期的相同?
}?
}?
如果測試一下equals方法,用類似的代碼,如下:?
public class MoneyTest extends TestCase { //TestCase的子類?
public void testEquals() { //把測試代碼放在testEquals中?
Money m12CHF= new Money(12, "CHF"); //本行和下一行進行一些初始化?
Money m14CHF= new Money(14, "CHF");?
Assert.assertTrue(!m12CHF.equals(null));//進行不同情況的測試?
Assert.assertEquals(m12CHF, m12CHF);?
Assert.assertEquals(m12CHF, new Money(12, "CHF")); // (1)?
Assert.assertTrue(!m12CHF.equals(m14CHF));?
}?
}?
當要同時進行測試Add和equals方法時,可以將它們的各自的初始化工作,合并到一起進行,形成測試基礎,用setUp初始化,用tearDown清除。如下:?
public class MoneyTest extends TestCase {//TestCase的子類?
private Money f12CHF;//提取公用的對象?
private Money f14CHF;?
protected void setUp() {//初始化公用對象?
f12CHF= new Money(12, "CHF");?
f14CHF= new Money(14, "CHF");?
}?
public void testEquals() {//測試equals方法的正確性?
Assert.assertTrue(!f12CHF.equals(null));?
Assert.assertEquals(f12CHF, f12CHF);?
Assert.assertEquals(f12CHF, new Money(12, "CHF"));?
Assert.assertTrue(!f12CHF.equals(f14CHF));?
}?
public void testSimpleAdd() {//測試add方法的正確性?
Money expected= new Money(26, "CHF");?
Money result= f12CHF.add(f14CHF);?
Assert.assertTrue(expected.equals(result));?
}?
}?
將以上三個中的任一個TestCase子類代碼保存到名為MoneyTest.java的文件里,并在文件首行增加?
import junit.framework.*;?
,都是可以運行的。關于Junit運行的問題很有意思,下面單獨說明。?
上面為解釋概念“測試基礎(fixture)”,引入了兩個對兩個方法的測試。命令模式與集成模式的本質區別是,前者一次只運行一個測試。?
集成模式?
利用TestSuite可以將一個TestCase子類中所有test***()方法包含進來一起運行,還可將TestSuite子類也包含進來,從而行成了一種等級關系。可以把TestSuite視為一個容器,可以盛放TestCase中的test***()方法,它自己也可以嵌套。這種體系架構,非常類似于現實中程序一步步開發一步步集成的現況。?
對上面的例子,有代碼如下:?
public class MoneyTest extends TestCase {//TestCase的子類?
....?
public static Test suite() {//靜態Test?
TestSuite suite= new TestSuite();//生成一個TestSuite?
suite.addTest(new MoneyTest("testEquals")); //加入測試方法?
suite.addTest(new MoneyTest("testSimpleAdd"));?
return suite;?
}?
}?
從Junit2.0開始,有列簡捷的方法:?
public class MoneyTest extends TestCase {//TestCase的子類?
....?
public static Test suite() {靜態Test?
return new TestSuite(MoneyTest.class); //以類為參數?
}?
}?
TestSuite見嵌套的例子,在后面應用案例中有。 ?
4、測試代碼的運行?
先說最常用的集成模式。?
測試代碼寫好以后,可以相應的類中寫main方法,用java命令直接運行;也可以不寫main方法,用Junit提供的運行器運行。Junit提供了textui,awtui和swingui三種運行器。?
以前面第2步中的AllTests運行為例,可有四種:?
java junit.textui.TestRunner junit.samples.AllTests?
java junit.awtui.TestRunner junit.samples.AllTests?
java junit.swingui.TestRunner junit.samples.AllTests?
java junit.samples.AllTests?
main方法中一般也都是簡單地用Runner調用suite(),當沒有main時,TestRunner自己以運行的類為參數生成了一個TestSuite.?
?
對于命令模式的運行,有兩種方法。?
靜態方法?
TestCase test= new MoneyTest("simple add") {?
public void runTest() {?
testSimpleAdd();?
}?
};?
動態方法?
TestCase test= new MoneyTest("testSimpleAdd");?
我試了一下,好象有問題,哪位朋友成功了,請指點我一下。確實可以。?
import junit.framework.*;?
public class MoneyTest extends TestCase {//TestCase的子類?
private Money f12CHF;//提取公用的對象?
private Money f14CHF;?
public MoneyTest(String name){?
super(name);?
}?
protected void setUp() {//初始化公用對象?
f12CHF= new Money(12, "CHF");?
f14CHF= new Money(14, "CHF");?
}?
public void testEquals() {//測試equals方法的正確性?
Assert.assertTrue(!f12CHF.equals(null));?
Assert.assertEquals(f12CHF, f12CHF);?
Assert.assertEquals(f12CHF, new Money(12, "CHF"));?
Assert.assertTrue(!f12CHF.equals(f14CHF));?
}?
public void testAdd() {//測試add方法的正確性?
Money expected= new Money(26, "CHF");?
Money result= f12CHF.add(f14CHF);?
Assert.assertTrue(expected.equals(result));?
}?
// public static void main(String[] args) {?
// TestCase test=new MoneyTest("simple add") {?
// public void runTest() {?
// testAdd();?
// }?
// };?
// junit.textui.TestRunner.run(test);?
// }?
public static void main(String[] args) {?
TestCase test=new MoneyTest("testAdd");?
junit.textui.TestRunner.run(test);?
}?
}?
再給一個靜態方法用集成測試的例子:?
public static Test suite() {?
TestSuite suite= new TestSuite();?
suite.addTest(?
new testCar("getWheels") {?
protected void runTest() { testGetWheels(); }?
}?
);?
suite.addTest(?
new testCar("getSeats") {?
protected void runTest() { testGetSeats(); }?
}?
);?
return suite;?
}?
5、應用案例?
Junit Primer例程,運行如下:?
java com.hedong.JunitLearning.Primer.ShoppingCartTest?
Ant+Junit+Mailto實現自動編譯、調試并發送結果的build.xml?
JUnit實施,寫得很棒,理解也深刻。例程運行如下:?
java com.hedong.JunitLearning.car.testCarNoJunit?
java junit.swingui.TestRunner com.hedong.JunitLearning.car.testCar?
Junit與log4j結合,阿菜的例程運行:?
cd acai?
ant junit?
6、一些問題?
有人在實踐基礎上總結出一些非常有價值的使用技巧,我沒有經過一一“測試”,暫列在此。?
不要用TestCase的構造函數初始化Fixture,而要用setUp()和tearDown()方法。?
不要依賴或假定測試運行的順序,因為JUnit利用Vector保存測試方法。所以不同的平臺會按不同的順序從Vector中取出測試方法。不知3.8中是不是還是如此,不過它提供的例子有一個是指定用VectorSuite的,如果不指定呢??
避免編寫有副作用的TestCase。例如:如果隨后的測試依賴于某些特定的交易數據,就不要提交交易數據。簡單的回滾就可以了。?
當繼承一個測試類時,記得調用父類的setUp()和tearDown()方法。?
將測試代碼和工作代碼放在一起,一邊同步編譯和更新。(使用Ant中有支持junit的task.)?
測試類和測試方法應該有一致的命名方案。如在工作類名前加上test從而形成測試類名。?
確保測試與時間無關,不要依賴使用過期的數據進行測試。導致在隨后的維護過程中很難重現測試。?
如果你編寫的軟件面向國際市場,編寫測試時要考慮國際化的因素。不要僅用母語的Locale進行測試。?
盡可能地利用JUnit提供地assert/fail方法以及異常處理的方法,可以使代碼更為簡潔。?
測試要盡可能地小,執行速度快。?
把測試程序建立在與被測對象相同的包中?
在你的原始代碼目錄中避免測試碼出現,可在一個源碼鏡像目錄中放測試碼?
在自己的應用程序包中包含一個TestSuite測試類?
7、相關資源下載?
以下jar包,我只是做了打包、編譯和調試的工作,供下載學習之用,相關的權利屬于原作者。?
可運行例程.jar?
Build.xml?
阿菜的例程?
Junit API 漢譯(pdf)?
8、未完成的任務?
httpunit?
cactus?
將Junit用鏈接池測試?
========
Eclipse中使用JUnit4進行單元測試具體操作
http://blog.csdn.net/youtellido/article/details/49457525簡述
Eclipse使用的版本Eclipse Mars(4.5)
JUnit是Java語言的單元測試框架
junit3與junit4的區別?
在junit3中,如果某個類是測試類,必須將其繼承類TestCase,如果某個方法是測試方法,必須讓這個方法以testXX開頭,如果希望指定某個測試方法運行之前運行某個初始化方法,這個方法的名稱必須是setUp,如果希望在某個測試方法運行之后運行某個釋放資源的方法,這個方法的名稱必須是tearDown。?
在junit4中,一個POJO類就是一個測試類,測試方法通過@Test來標識,初始化方法通過@Before來標識,釋放資源的方法通過@After來標識,但是為了讓junit4的測試類在junit3中也可以使用,習慣于把初始化方法命名為setUp,釋放資源的方法命名為tearDown。Test中的測試方法一般以Test來開始。其中標識為Before注解的方法,每次運行測試類,都會執行標識為@After與@Before的方法。?
4.在junit4中提供了一個Assert的類,這個類中有大量的靜態方法進行斷言的處理,在junit3中由于繼承了TestCase,這個TestCase就可以直接assert,而junit4中需要先引入Assert類。
具體操作
1.在Eclipse中新建一個工程TestUnit,如圖?
圖1?
2.編寫一個測試類Number.Java?
圖2
package test;
public class Number {
? ? public int add(int a,int b){
? ? ? ? return a+b;
? ? }
? ? public int min(int a,int b){
? ? ? ? if(a>b){
? ? ? ? ? ? return a-b;
? ? ? ? }
? ? ? ? return b-a;
? ? }
}
3.選中類Number.java,右鍵New,選擇JUnit Test Case,如圖?
圖3
4.在打開的New JUnit Test Case對話框中,按照如下圖所示的方式選擇配置,基本保持默認配置,如圖?
圖4?
5.Next,選擇要測試的方法,這里全選,如圖?
圖5?
6.Finish,彈出添加JUnit4 Library的對話框,OK。?
圖6?
7.之后生成NumberTest.java,如圖?
圖7
NumberTest.java
package test;
import static org.junit.Assert.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class NumberTest {
? ? @Before
? ? public void setUp() throws Exception {
? ? }
? ? @After
? ? public void tearDown() throws Exception {
? ? }
? ? @Test
? ? public void testAdd() {
? ? ? ? fail("Not yet implemented");
? ? }
? ? @Test
? ? public void testMin() {
? ? ? ? fail("Not yet implemented");
? ? }
}
以上是默認生成的測試框架,接下來我們就要給測試方法添加代碼了。?
8.更改代碼如下
package test;
import static org.junit.Assert.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class NumberTest {
? ? private Number num;
? ? @Before
? ? public void setUp() throws Exception {
? ? ? ? num=new Number();
? ? }
? ? @After
? ? public void tearDown() throws Exception {
? ? }
? ? @Test
? ? public void testAdd() {
? ? ? ? int res=num.add(2, 3);
? ? ? ? assertEquals(5, res);
? ? }
? ? @Test
? ? public void testMin() {
? ? ? ? int res=num.min(8, 3);
? ? ? ? assertEquals(5, res);
? ? }
}
9.選中NumberTest.java,右鍵,Run As–>JUnit Test,得到測試結果,當前結果表示測試通過。如圖?
圖8
========
Java單元測試(Junit+Mock+代碼覆蓋率)
http://www.cnblogs.com/AloneSword/p/4109407.html單元測試是編寫測試代碼,用來檢測特定的、明確的、細顆粒的功能。單元測試并不一定保證程序功能是正確的,更不保證整體業務是準備的。
單元測試不僅僅用來保證當前代碼的正確性,更重要的是用來保證代碼修復、改進或重構之后的正確性。
一般來說,單元測試任務包括
接口功能測試:用來保證接口功能的正確性。
局部數據結構測試(不常用):用來保證接口中的數據結構是正確的
比如變量有無初始值
變量是否溢出
邊界條件測試
變量沒有賦值(即為NULL)
變量是數值(或字符)
主要邊界:最小值,最大值,無窮大(對于DOUBLE等)
溢出邊界(期望異常或拒絕服務):最小值-1,最大值+1
臨近邊界:最小值+1,最大值-1
變量是字符串
引用“字符變量”的邊界
空字符串
對字符串長度應用“數值變量”的邊界
變量是集合
空集合
對集合的大小應用“數值變量”的邊界
調整次序:升序、降序
變量有規律
比如對于Math.sqrt,給出n^2-1,和n^2+1的邊界
所有獨立執行通路測試:保證每一條代碼,每個分支都經過測試
代碼覆蓋率
語句覆蓋:保證每一個語句都執行到了
判定覆蓋(分支覆蓋):保證每一個分支都執行到
條件覆蓋:保證每一個條件都覆蓋到true和false(即if、while中的條件語句)
路徑覆蓋:保證每一個路徑都覆蓋到
相關軟件
Cobertura:語句覆蓋
Emma: Eclipse插件Eclemma
各條錯誤處理通路測試:保證每一個異常都經過測試
JUNIT
JUnit是Java單元測試框架,已經在Eclipse中默認安裝。目前主流的有JUnit3和JUnit4。JUnit3中,測試用例需要繼承TestCase類。JUnit4中,測試用例無需繼承TestCase類,只需要使用@Test等注解。
Junit3
先看一個Junit3的樣例
// 測試java.lang.Math ?
// 必須繼承TestCase ?
public class Junit3TestCase extends TestCase { ?
? ? public Junit3TestCase() { ?
? ? ? ? super(); ?
? ? } ?
? ??
? ? ? ? // 傳入測試用例名稱 ?
? ? public Junit3TestCase(String name) { ?
? ? ? ? super(name); ?
? ? } ?
? ?
? ? ? ? // 在每個Test運行之前運行 ?
? ? @Override ?
? ? protected void setUp() throws Exception { ?
? ? ? ? System.out.println("Set up"); ?
? ? } ?
? ? ? ? // 測試方法。 ?
? ? ? ? // 方法名稱必須以test開頭,沒有參數,無返回值,是公開的,可以拋出異常 ?
? ? ? ? // 也即類似public void testXXX() throws Exception {} ?
? ? public void testMathPow() { ?
? ? ? ? System.out.println("Test Math.pow"); ?
? ? ? ? Assert.assertEquals(4.0, Math.pow(2.0, 2.0)); ?
? ? } ?
? ?
? ? public void testMathMin() { ?
? ? ? ? System.out.println("Test Math.min"); ?
? ? ? ? Assert.assertEquals(2.0, Math.min(2.0, 4.0)); ?
? ? } ?
? ?
? ? ? ? // 在每個Test運行之后運行 ?
? ? @Override ?
? ? protected void tearDown() throws Exception { ?
? ? ? ? System.out.println("Tear down"); ?
? ? } ?
} ?
?
如果采用默認的TestSuite,則測試方法必須是public void testXXX() [throws Exception] {}的形式,并且不能存在依賴關系,因為測試方法的調用順序是不可預知的。
上例執行后,控制臺會輸出
Set up ?
Test Math.pow ?
Tear down ?
Set up ?
Test Math.min ?
Tear down ?
?
從中,可以猜測到,對于每個測試方法,調用的形式是:
testCase.setUp(); ?
testCase.testXXX(); ?
testCase.tearDown(); ? ??
?
運行測試方法
在Eclipse中,可以直接在類名或測試方法上右擊,在彈出的右擊菜單中選擇Run As -> JUnit Test。
在Mvn中,可以直接通過mvn test命令運行測試用例。
也可以通過Java方式調用,創建一個TestCase實例,然后重載runTest()方法,在其方法內調用測試方法(可以多個)。
TestCase test = new Junit3TestCase("mathPow") { ?
? ? ? ? // 重載 ?
? ? protected void runTest() throws Throwable { ?
? ? ? ? testMathPow(); ?
? ? }; ?
}; ?
test.run(); ?
?
更加便捷地,可以在創建TestCase實例時直接傳入測試方法名稱,JUnit會自動調用此測試方法,如
TestCase test = new Junit3TestCase("testMathPow"); ?
test.run(); ?
?
Junit TestSuite
TestSuite是測試用例套件,能夠運行過個測試方法。如果不指定TestSuite,會創建一個默認的TestSuite。默認TestSuite會掃描當前內中的所有測試方法,然后運行。
如果不想采用默認的TestSuite,則可以自定義TestSuite。在TestCase中,可以通過靜態方法suite()返回自定義的suite。
import junit.framework.Assert; ?
import junit.framework.Test; ?
import junit.framework.TestCase; ?
import junit.framework.TestSuite; ?
? ?
public class Junit3TestCase extends TestCase { ?
? ? ? ? //... ?
? ? public static Test suite() { ?
? ? ? ? System.out.println("create suite"); ?
? ? ? ? TestSuite suite = new TestSuite(); ?
? ? ? ? suite.addTest(new Junit3TestCase("testMathPow")); ?
? ? ? ? return suite; ?
? ? } ?
} ?
?
允許上述方法,控制臺輸出
寫道
create suite
Set up
Test Math.pow
Tear down
?
并且只運行了testMathPow測試方法,而沒有運行testMathMin測試方法。通過顯式指定測試方法,可以控制測試執行的順序。
也可以通過Java的方式創建TestSuite,然后調用TestCase,如?
// 先創建TestSuite,再添加測試方法 ?
TestSuite testSuite = new TestSuite(); ?
testSuite.addTest(new Junit3TestCase("testMathPow")); ?
? ?
// 或者 傳入Class,TestSuite會掃描其中的測試方法。 ?
TestSuite testSuite = new TestSuite(Junit3TestCase.class,Junit3TestCase2.class,Junit3TestCase3.class); ?
? ?
// 運行testSuite ?
TestResult testResult = new TestResult(); ?
testSuite.run(testResult); ?
?
testResult中保存了很多測試數據,包括運行測試方法數目(runCount)等。
JUnit4
與JUnit3不同,JUnit4通過注解的方式來識別測試方法。目前支持的主要注解有:
@BeforeClass 全局只會執行一次,而且是第一個運行
@Before 在測試方法運行之前運行
@Test 測試方法
@After 在測試方法運行之后允許
@AfterClass 全局只會執行一次,而且是最后一個運行
@Ignore 忽略此方法
下面舉一個樣例:
import org.junit.After; ?
import org.junit.AfterClass; ?
import org.junit.Assert; ?
import org.junit.Before; ?
import org.junit.BeforeClass; ?
import org.junit.Ignore; ?
import org.junit.Test; ?
? ?
public class Junit4TestCase { ?
? ?
? ? @BeforeClass ?
? ? public static void setUpBeforeClass() { ?
? ? ? ? System.out.println("Set up before class"); ?
? ? } ?
? ?
? ? @Before ?
? ? public void setUp() throws Exception { ?
? ? ? ? System.out.println("Set up"); ?
? ? } ?
? ?
? ? @Test ?
? ? public void testMathPow() { ?
? ? ? ? System.out.println("Test Math.pow"); ?
? ? ? ? Assert.assertEquals(4.0, Math.pow(2.0, 2.0), 0.0); ?
? ? } ?
? ?
? ? @Test ?
? ? public void testMathMin() { ?
? ? ? ? System.out.println("Test Math.min"); ?
? ? ? ? Assert.assertEquals(2.0, Math.min(2.0, 4.0), 0.0); ?
? ? } ?
? ?
? ? ? ? // 期望此方法拋出NullPointerException異常 ?
? ? @Test(expected = NullPointerException.class) ?
? ? public void testException() { ?
? ? ? ? System.out.println("Test exception"); ?
? ? ? ? Object obj = null; ?
? ? ? ? obj.toString(); ?
? ? } ?
? ?
? ? ? ? // 忽略此測試方法 ?
? ? @Ignore ?
? ? @Test ?
? ? public void testMathMax() { ?
? ? ? ? ? Assert.fail("沒有實現"); ?
? ? } ?
? ? ? ? // 使用“假設”來忽略測試方法 ?
? ? @Test ?
? ? public void testAssume(){ ?
? ? ? ? System.out.println("Test assume"); ?
? ? ? ? ? ? ? ? // 當假設失敗時,則會停止運行,但這并不會意味測試方法失敗。 ?
? ? ? ? Assume.assumeTrue(false); ?
? ? ? ? Assert.fail("沒有實現"); ?
? ? } ?
? ?
? ? @After ?
? ? public void tearDown() throws Exception { ?
? ? ? ? System.out.println("Tear down"); ?
? ? } ?
? ?
? ? @AfterClass ?
? ? public static void tearDownAfterClass() { ?
? ? ? ? System.out.println("Tear down After class"); ?
? ? } ?
? ?
} ?
?
如果細心的話,會發現Junit3的package是junit.framework,而Junit4是org.junit。
執行此用例后,控制臺會輸出
寫道
Set up before class
Set up
Test Math.pow
Tear down
Set up
Test Math.min
Tear down
Set up
Test exception
Tear down
Set up
Test assume
Tear down
Tear down After class
?
可以看到,執行次序是@BeforeClass -> @Before -> @Test -> @After -> @Before -> @Test -> @After -> @AfterClass。@Ignore會被忽略。
運行測試方法
與Junit3類似,可以在Eclipse中運行,也可以通過mvn test命令運行。
Assert
Junit3和Junit4都提供了一個Assert類(雖然package不同,但是大致差不多)。Assert類中定義了很多靜態方法來進行斷言。列表如下:
assertTrue(String message, boolean condition) 要求condition == true
assertFalse(String message, boolean condition) 要求condition == false
fail(String message) 必然失敗,同樣要求代碼不可達
assertEquals(String message, XXX expected,XXX actual) 要求expected.equals(actual)
assertArrayEquals(String message, XXX[] expecteds,XXX [] actuals) 要求expected.equalsArray(actual)
assertNotNull(String message, Object object) 要求object!=null
assertNull(String message, Object object) 要求object==null
assertSame(String message, Object expected, Object actual) 要求expected == actual
assertNotSame(String message, Object unexpected,Object actual) 要求expected != actual
assertThat(String reason, T actual, Matcher matcher) 要求matcher.matches(actual) == true
Mock/Stub
Mock和Stub是兩種測試代碼功能的方法。Mock測重于對功能的模擬。Stub測重于對功能的測試重現。比如對于List接口,Mock會直接對List進行模擬,而Stub會新建一個實現了List的TestList,在其中編寫測試的代碼。
強烈建議優先選擇Mock方式,因為Mock方式下,模擬代碼與測試代碼放在一起,易讀性好,而且擴展性、靈活性都比Stub好。
比較流行的Mock有:
JMock
EasyMock
Mockito
powermock
其中EasyMock和Mockito對于Java接口使用接口代理的方式來模擬,對于Java類使用繼承的方式來模擬(也即會創建一個新的Class類)。Mockito支持spy方式,可以對實例進行模擬。但它們都不能對靜態方法和final類進行模擬,powermock通過修改字節碼來支持了此功能。
EasyMock
IBM上有幾篇介紹EasyMock使用方法和原理的文章:EasyMock 使用方法與原理剖析,使用 EasyMock 更輕松地進行測試。
EasyMock把測試過程分為三步:錄制、運行測試代碼、驗證期望。
錄制過程大概就是:期望method(params)執行times次(默認一次),返回result(可選),拋出exception異常(可選)。
驗證期望過程將會檢查方法的調用次數。
一個簡單的樣例是:?
@Test ?
public void testListInEasyMock() { ?
? ? List list = EasyMock.createMock(List.class); ?
? ? // 錄制過程 ?
? ?
? ? // 期望方法list.set(0,1)執行2次,返回null,不拋出異常 ?
? ? expect1: EasyMock.expect(list.set(0, 1)).andReturn(null).times(2); ?
? ? // 期望方法list.set(0,1)執行1次,返回null,不拋出異常 ?
? ? expect2: EasyMock.expect(list.set(0, 1)).andReturn(1); ?
? ?
? ? // 執行測試代碼 ?
? ? EasyMock.replay(list); ?
? ? ? ? // 執行list.set(0,1),匹配expect1期望,會返回null ?
? ? Assert.assertNull(list.set(0, 1)); ?
? ? ? ? // 執行list.set(0,1),匹配expect1(因為expect1期望執行此方法2次),會返回null ?
? ? Assert.assertNull(list.set(0, 1)); ?
? ? ? ? // 執行list.set(0,1),匹配expect2,會返回1 ?
? ? Assert.assertEquals(1, list.set(0, 1)); ?
? ?
? ? // 驗證期望 ?
? ? EasyMock.verify(list); ?
} ?
?
EasyMock還支持嚴格的檢查,要求執行的方法次序與期望的完全一致。
Mockito
Mockito是Google Code上的一個開源項目,Api相對于EasyMock更好友好。與EasyMock不同的是,Mockito沒有錄制過程,只需要在“運行測試代碼”之前對接口進行Stub,也即設置方法的返回值或拋出的異常,然后直接運行測試代碼,運行期間調用Mock的方法,會返回預先設置的返回值或拋出異常,最后再對測試代碼進行驗證。可以查看此文章了解兩者的不同。
官方提供了很多樣例,基本上包括了所有功能,可以去看看。
這里從官方樣例中摘錄幾個典型的:
驗證調用行為
import static org.mockito.Mockito.*; ?
? ?
//創建Mock ?
List mockedList = mock(List.class); ?
? ?
//使用Mock對象 ?
mockedList.add("one"); ?
mockedList.clear(); ?
? ?
//驗證行為 ?
verify(mockedList).add("one"); ?
verify(mockedList).clear(); ?
?
?
對Mock對象進行Stub
//也可以Mock具體的類,而不僅僅是接口 ?
LinkedList mockedList = mock(LinkedList.class); ?
? ?
//Stub ?
when(mockedList.get(0)).thenReturn("first"); // 設置返回值 ?
when(mockedList.get(1)).thenThrow(new RuntimeException()); // 拋出異常 ?
? ?
//第一個會打印 "first" ?
System.out.println(mockedList.get(0)); ?
? ?
//接下來會拋出runtime異常 ?
System.out.println(mockedList.get(1)); ?
? ?
//接下來會打印"null",這是因為沒有stub get(999) ?
System.out.println(mockedList.get(999)); ?
? ??
// 可以選擇性地驗證行為,比如只關心是否調用過get(0),而不關心是否調用過get(1) ?
verify(mockedList).get(0); ??
?
代碼覆蓋率
比較流行的工具是Emma和Jacoco,Ecliplse插件有eclemma。eclemma2.0之前采用的是Emma,之后采用的是Jacoco。這里主要介紹一下Jacoco。Eclmama由于是Eclipse插件,所以非常易用,就不多做介紹了。
Jacoco
Jacoco可以嵌入到Ant、Maven中,也可以使用Java Agent技術監控任意Java程序,也可以使用Java Api來定制功能。
Jacoco會監控JVM中的調用,生成監控結果(默認保存在jacoco.exec文件中),然后分析此結果,配合源代碼生成覆蓋率報告。需要注意的是:監控和分析這兩步,必須使用相同的Class文件,否則由于Class不同,而無法定位到具體的方法,導致覆蓋率均為0%。
Java Agent嵌入
首先,需要下載jacocoagent.jar文件,然后在Java程序啟動參數后面加上 -javaagent:[yourpath/]jacocoagent.jar=[option1]=[value1],[option2]=[value2],具體的options可以在此頁面找到。默認會在JVM關閉時(注意不能是kill -9),輸出監控結果到jacoco.exec文件中,也可以通過socket來實時地輸出監控報告(可以在Example代碼中找到簡單實現)。
Java Report
可以使用Ant、Mvn或Eclipse來分析jacoco.exec文件,也可以通過API來分析。
public void createReport() throws Exception { ?
? ? ? ? ? ? // 讀取監控結果 ?
? ? final FileInputStream fis = new FileInputStream(new File("jacoco.exec")); ?
? ? final ExecutionDataReader executionDataReader = new ExecutionDataReader(fis); ?
? ? ? ? ? ? // 執行數據信息 ?
? ? ExecutionDataStore executionDataStore = new ExecutionDataStore(); ?
? ? ? ? ? ? // 會話信息 ?
? ? SessionInfoStore sessionInfoStore = new SessionInfoStore(); ?
? ?
? ? executionDataReader.setExecutionDataVisitor(executionDataStore); ?
? ? executionDataReader.setSessionInfoVisitor(sessionInfoStore); ?
? ?
? ? while (executionDataReader.read()) { ?
? ? } ?
? ?
? ? fis.close(); ?
? ? ? ? ? ? ? ?
? ? ? ? ? ? // 分析結構 ?
? ? ? ? ? ? final CoverageBuilder coverageBuilder = new CoverageBuilder(); ?
? ? final Analyzer analyzer = new Analyzer(executionDataStore, coverageBuilder); ?
? ?
? ? ? ? ? ? // 傳入監控時的Class文件目錄,注意必須與監控時的一樣 ?
? ? File classesDirectory = new File("classes"); ?
? ? analyzer.analyzeAll(classesDirectory); ?
? ?
? ? IBundleCoverage bundleCoverage = coverageBuilder.getBundle("Title"); ?
? ? ? ? ? ? // 輸出報告 ?
? ? ? ? File reportDirectory = new File("report"); // 報告所在的目錄 ?
? ? final HTMLFormatter htmlFormatter = new HTMLFormatter(); ?// HTML格式 ?
? ? final IReportVisitor visitor = htmlFormatter.createVisitor(new FileMultiReportOutput(reportDirectory)); ?
? ? ? ? ? ? // 必須先調用visitInfo ?
? ? visitor.visitInfo(sessionInfoStore.getInfos(), executionDataStore.getContents()); ?
? ? File sourceDirectory = new File("src"); // 源代碼目錄 ?
? ? ? ? ? ? // 遍歷所有的源代碼 ?
? ? ? ? ? ? // 如果不執行此過程,則在報告中只能看到方法名,但是無法查看具體的覆蓋(因為沒有源代碼頁面) ?
? ? visitor.visitBundle(bundleCoverage, new DirectorySourceFileLocator(sourceDirectory, "utf-8", 4)); ?
? ? ? ? ? ? // 執行完畢 ?
? ? visitor.visitEnd(); ?
} ??
========
菜鳥學Java(二十一)——如何更好的進行單元測試——JUnit
測試在軟件生命周期中的重要性,不用我多說想必大家也都非常清楚。軟件測試有很多分類,從測試的方法上可分為:黑盒測試、白盒測試、靜態測試、動態測試等;從軟件開發的過程分為:單元測試、集成測試、確認測試、驗收、回歸等。
在眾多的分類中,與開發人員關系最緊密的莫過于單元測試了。像其他種類的測試基本上都是由專門的測試人員來完成,只有單元測試是完全由開發人員來完成的。那么今天我們就來說說什么是單元測試,為什么要進行單元測試,以及如更好的何進行單元測試。
什么是單元測試?
單元測試(unit testing),是指對軟件中的最小可測試單元進行檢查和驗證。比如我們可以測試一個類,或者一個類中的一個方法。
為什么要進行單元測試?
為什么要進行單元測試?說白了就是單元測試有什么好處,其實測試的好處無非就是減少bug、提高代碼質量、使代碼易于維護等。單元測試有什么好處請看一下百度百科中歸納的四條:
1、它是一種驗證行為。
程序中的每一項功能都是測試來驗證它的正確性。它為以后的開發提供支援。就算是開發后期,我們也可以輕松的增加功能或更改程序結構,而不用擔心這個過程中會破壞重要的東西。而且它為代碼的重構提供了保障。這樣,我們就可以更自由的對程序進行改進。
2、它是一種設計行為。
編寫單元測試將使我們從調用者觀察、思考。特別是先寫測試(test-first),迫使我們把程序設計成易于調用和可測試的,即迫使我們解除軟件中的耦合。
3、它是一種編寫文檔的行為。
單元測試是一種無價的文檔,它是展示函數或類如何使用的最佳文檔。這份文檔是可編譯、可運行的,并且它保持最新,永遠與代碼同步。
4、它具有回歸性。
自動化的單元測試避免了代碼出現回歸,編寫完成之后,可以隨時隨地的快速運行測試。
如何更好的進行單元測試?
在討論如何更好的進行單元測試之前,先來看看我們以前是怎么測試代碼的。
以前是這樣測試程序的:
[java] view plain copy 在CODE上查看代碼片派生到我的代碼片
public int add(int x,int y) { ?
? ? return x + y; ?
} ?
? ? ??
public static void main(String args[]) { ?
? ? int z = new Junit().add(2, 3); ?
? ? System.out.println(z); ?
} ?
如上面所示,在測試我們寫好的一個方法時,通常是用一個main方法調用一下我們要測試的方法,然后將結果打印一下。現在看來這種方式已經非常out了,所以出現了很多單元測試的工具,如:JUnit、TestNG等。借助它們可以讓我們的單元測試變得非常方便、高效。今天就說說如何利用JUnit進行單元測試。
我們新建一個Java Project以便進行演示,至于Java Project怎么創建我就不在此贅述了,如果連怎么建Java Project,那你還不適合看這篇文章。建好以后在該項目的“src”目錄上右擊,選擇new——》JUnit Test Case,然后按下圖填寫必要信息:
填寫好包名和類名(選擇New JUnit 4 Test),點擊最下面的那個“Browse”按鈕來選擇需要測試的類:
手動輸入我們要測試的類,選擇該類,點擊“OK”,回到第一張圖的界面,然后點擊“Next”,來到下圖:
勾選要測試的方法,點擊“Finish”,這樣我們的JUnit測試實例就建好了。然后就可以寫具體的測試了:
package com.tgb.junit.test; ?
??
//靜態引入 ?
import static org.junit.Assert.*; ?
import static org.hamcrest.Matchers.*; ?
??
import org.junit.Test; ?
??
import com.tgb.junit.Junit; ?
??
public class JUnitTest { ?
??
? ? @Test ?
? ? public void testAdd() { ?
? ? ? ? int z = new ?Junit().add(2, 3); ?
? ? ? ? assertThat(z , is(5)); ?
? ? } ?
??
? ? @Test ?
? ? public void testDivide() { ?
? ? ? ? int z = new Junit().divide(4, 2); ? ? ? ??
? ? ? ? assertThat(z, is(2)); ?
? ? } ?
} ?
寫好以后,右擊該類選擇“Run As”——》“JUnit Test”,出現下圖代表測試通過:
到這里,可能有人會有疑問,JUnit跟用main方法測試有什么區別呢?
首先,JUnit的結果更加直觀,直接根據狀態條的顏色即可判斷測試是否通過,而用main方法你需要去檢查他的輸出結果,然后跟自己的期望結果進行對比,才能知道是否測試通過。有一句話能夠很直觀的說明這一點——keeps the bar green to keeps the code clean。意思就是說,只要狀態條是綠色的,那么你的代碼就是正確的。
第二點,JUnit讓我們同時運行多個測試變得非常方便,下面就演示一下如何進行多實例測試:
首先我們要再建一個待測試類,然后再建一個對應的JUnit測試實例,步驟略。然后在我們測試實例的包上右擊選擇“Run As”——》“Run Configurations”,如下圖;
選擇第二項“Run all tests in the selected project, package or source folder”,然后點擊“Run”效果如下:
可以看到,我們本次測試了兩個類,共三個方法,這種方便的效果在測試實例越多的情況下,體現的越明顯。至于main方法運行多個測試,想想就覺得非常麻煩,這里就不演示了。
JUnit除了可以測試這些簡單的小程序,還可以測試Struts、JDBC等等,這里只是用這個小程序做過簡單的介紹。本實例使用的是hamcrest斷言,而沒有使用老的斷言,因為hamcrest斷言更加接近自然語言的表達方式,更易于理解。
本實例需要引入以下三個jar包:
hamcrest-core-1.3.jar
hamcrest-library-1.3.jar
junit-4.10.jar
最后附上常用hamcrest斷言的使用說明:
[java] view plain copy 在CODE上查看代碼片派生到我的代碼片
數值類型 ?
//n大于1并且小于15,則測試通過 ?
assertThat( n, allOf( greaterThan(1), lessThan(15) ) ); ?
//n大于16或小于8,則測試通過 ?
assertThat( n, anyOf( greaterThan(16), lessThan(8) ) ); ?
//n為任何值,都測試通過 ?
assertThat( n, anything() ); ?
//d與3.0的差在±0.3之間,則測試通過 ?
assertThat( d, closeTo( 3.0, 0.3 ) ); ?
//d大于等于5.0,則測試通過 ?
assertThat( d, greaterThanOrEqualTo (5.0) ); ?
//d小于等于16.0,則測試通過 ?
assertThat( d, lessThanOrEqualTo (16.0) ); ?
??
字符類型 ?
//str的值為“tgb”,則測試通過 ?
assertThat( str, is( "tgb" ) ); ?
//str的值不是“tgb”,則測試通過 ?
assertThat( str, not( "tgb" ) ); ?
//str的值包含“tgb”,則測試通過 ?
assertThat( str, containsString( "tgb" ) ); ?
//str以“tgb”結尾,則測試通過 ?
assertThat( str, endsWith("tgb" ) ); ??
//str以“tgb”開頭,則測試通過 ?
assertThat( str, startsWith( "tgb" ) ); ??
//str忽略大小寫后,值為“tgb”,則測試通過 ?
assertThat( str, equalToIgnoringCase( "tgb" ) ); ??
//str忽略空格后,值為“tgb”,則測試通過 ?
assertThat( str, equalToIgnoringWhiteSpace( "tgb" ) ); ?
//n與nExpected相等,則測試通過(對象之間) ?
assertThat( n, equalTo( nExpected ) ); ??
??
collection類型 ?
//map中包含key和value為“tgb”的鍵值對,則測試通過 ?
assertThat( map, hasEntry( "tgb", "tgb" ) ); ?
//list中包含“tgb”元素,則測試通過 ?
assertThat( iterable, hasItem ( "tgb" ) ); ?
//map中包含key為“tgb”的元素,則測試通過 ?
assertThat( map, hasKey ( "tgb" ) ); ?
//map中包含value為“tgb”的元素,則測試通過 ?
assertThat( map, hasValue ( "tgb" ) ); ?
========
java編程之單元測試(Junit)實例分析(附實例源碼)
http://www.jb51.net/article/74976.htm這篇文章主要介紹了java編程之單元測試(Junit),結合實例形式較為詳細的分析總結了Java單元測試的原理、步驟及相關注意事項,并附帶了完整代碼供讀者下載參考,需要的朋友可以參考下
..本文實例講述了java編程之單元測試。分享給大家供大家參考,具體如下:
完整實例代碼代碼點擊此處本站下載。
在有些時候,我們需要對我們自己編寫的代碼進行單元測試(好處是,減少后期維護的精力和費用),這是一些最基本的模塊測試。當然,在進行單元測試的同時也必然得清楚我們測試的代碼的內部邏輯實現,這樣在測試的時候才能清楚地將我們希望代碼邏輯實現得到的結果和測試實際得到的結果進行驗證對比。
廢話少說,上代碼:
首先創建一個java工程,在工程中創建一個被單元測試的Student數據類,如下:
package com.phicomme.hu; ?public class Student ?{ ? private String name; ? private String sex; ? private int high; ? private int age; ? private String school; ? public Student(String name, String sex ,int high, int age, String school) ? { ? ?this.name = name; ? ?this.sex = sex; ? ?this.high = high; ? ?this.age = age; ? ?this.school = school; ? } ? public String getName() ? { ? ?return name; ? } ? public void setName(String name) ? { ? ?this.name = name; ? } ? public String getSex() ? { ? ?return sex; ? } ? public void setSex(String sex) ? { ? ?this.sex = sex; ? } ? public int getHigh() ? { ? ?return high; ? } ? public void setHigh(int high) ? { ? ?this.high = high; ? } ? public int getAge() ? { ? ?return age; ? } ? public boolean setAge(int age) ? { ? ?if (age >25) ? ?{ ? ? return false; ? ?} ? ?else ? { ? ? this.age = age; ? ? return true; ? ?} ? ? ? } ? public String getSchool() ? { ? ?return school; ? } ? public void setSchool(String school) ? { ? ?this.school = school; ? } ?}?
在eclipse下單元測試這個類:
首先導入Junit包:選中java工程,點擊鼠標右鍵--->選擇properties---->在窗口中選Java Build Path---->在右側點擊Add Library---->在彈出的窗口列表中選中Junit---->下一步----->Junit 4(我用的是Junit 4)---->finish
這樣Junit 4包就導完了,接下來就是創建測試類:
將測試類和被測試類放在不同的包中(也可以放在同一個包中,此處只是為了區別),代碼如下:
測試類1:
package com.phicomme.test; ?import com.phicomme.hu.Student; ?import junit.framework.TestCase; ?public class StudentTest01 extends TestCase ?{ ? Student testStudent; ? //此方法在執行每一個測試方法之前(測試用例)之前調用 ? @Override ?protected void setUp() throws Exception ? { ? ?// TODO Auto-generated method stub ? ?super.setUp(); ? ?testStudent = new Student("djm", "boy", 178, 24, "華東政法"); ? ?System.out.println("setUp()"); ? } ? //此方法在執行每一個測試方法之后調用 ? @Override ?protected void tearDown() throws Exception ? { ? ?// TODO Auto-generated method stub ? ?super.tearDown(); ? ?System.out.println("tearDown()"); ? } ? //測試用例,測試Person對象的getSex()方法 ? public void testGetSex() ? { ? ?assertEquals("boy", testStudent.getSex()); ? ?System.out.println("testGetSex()"); ? } ? //測試Person對象的getAge()方法 ? public void testGetAge() ? { ? ?assertEquals(24, testStudent.getAge()); ? ?System.out.println("testGetAge()"); ? } ?}?
測試類2:
package com.phicomme.test; ?import junit.framework.TestCase; ?import com.phicomme.hu.Student; ?public class StudentTest extends TestCase ?{ ? private Student testStudent; ? @Override ?protected void setUp() throws Exception ? { ? ?// TODO Auto-generated method stub ? ?super.setUp(); ? ?testStudent = new Student("steven_hu", "boy", 170 , 23, "上海理工"); ? } ? @Override ?protected void tearDown() throws Exception ? { ? ?// TODO Auto-generated method stub ? ?super.tearDown(); ? } ? public void testSetage() ? { ? ?assertTrue(testStudent.setAge(21)); ? } ? public void testGetSchool() ? { ? ?//預期值和實際值不一樣,測試時出現失敗(Failure) ? ?assertEquals("南昌大學", testStudent.getSchool()); ? } ? public void testGetName() ? { ? ?assertEquals("hdy", testStudent.getName()); ? } ?}?
當然,如果同時需要一起測試以上這兩個測試類,可以通過TestSuite類實現,它相當于是一個套件,可以把所有測試類添進來一起運行測試;
代碼如下:
package com.phicomme.test; ?import com.phicomme.hu.StudentTest02; ?import junit.framework.Test; ?import junit.framework.TestSuite; ?public class AllTest ?{ ? //static PersonTest p = new PersonTest(); ? //static PersonTest p1 = new PersonTest(); ? public static Test suite() ? { ? ?TestSuite suite = new TestSuite("Test for com.phicomme.test"); ? ?//suite.addTest(p); ? ?//suite.addTest(p1); ? ?suite.addTestSuite(StudentTest.class); ? ?suite.addTestSuite(StudentTest01.class); ? ?return suite; ? } ?}?
最后,分別測試以上三個類(選中需要測試的類---->鼠標右鍵---->Run As---->Junit Test):
StudentTest類的測試結果圖:
StudentTest01類的測試結果圖:
AllTest類的測試結果圖:
有關java的測試就講到這里,希望對大家有幫助,有時間也會接著講講有關android的單元測試,和在手機上實現編寫一個UI界面替代eclipse如上圖中的測試界面;
========
總結
以上是生活随笔為你收集整理的java单元测试总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: tomcat 内存配置学习总结
- 下一篇: C++ boost 实例学习