打樁(mock)是單元測試的重要內容和難點,學好打樁的技術,做單元測試基本就沒什么困難了。
mock有兩種,一種是靜態的,一種是動態的。靜態的就是在寫測試代碼之前根據需要打樁的類生成另外一個類,這個類就是mock object。動態的就是mock object是在測試代碼運行的時候才生成的。所以很明顯,動態打樁比靜態打樁要方便地多。本章就是介紹動態打樁的工具。
早期的動態mock工具只能夠mock接口,而不能夠mock類;現在的mock工具無論是mock接口還是類都能夠輕松完成了。
easymock就是其中的佼佼者,easymock現在已經是2.2版本了,本文使用的是easymock1.2_Java1.5版本。使用easymock能夠輕松的mock任何接口,但如果想mock類,那還需要另外一個lib,就是easymockclassextension,使用了這個工具,你就能輕松地mock你要的任何類了。本文使用的是easymockclassextension1.2,使用這個庫必須要cglib這個jar包,而cglib又需要asm的jar包,所以要搭建好環境還得花些時間,不過當你把環境搞好之后,你便會發現物超所值,從此以后打樁無難事了。cglib和asm的版本一定要適配,要不然不能正常工作,幸好cglib的網站已經提供了cglib和asm的綁定包,本文所用的是cglib-nodep-2.2_beta1版本。
本章先對如何對接口進行打樁示例,下一章再對如何對接口進行打樁示例。
先看欲打樁接口代碼:
/************************************************************
* Project Name: lhjTest
* File Name : CoolInterface.java
* File Desc : CoolInterface.java
* Author : Administrator
* Create : 2007-3-25
* Modify:
***********************************************************/
package org.lhj.cool.junit;

/**
* @author Administrator
*/
public interface CoolInterface
{
String reverseString(String inputStr) throws NullPointerException;
}
該接口很簡單,只要一個reverString的方法。
下面看看欲測試類(使用了CoolInterface)的代碼:
/************************************************************
* Project Name: lhjTest
* File Name : UseCoolInterface.java
* File Desc : UseCoolInterface.java
* Author : Administrator
* Create : 2007-3-25
* Modify:
***********************************************************/
package org.lhj.cool.mock;

import org.lhj.cool.junit.CoolInterface;

/**
* @author Administrator
*/
public class UseCoolInterface
{
private CoolInterface cool;
public void setCoolInterface(CoolInterface cool)
{
this.cool = cool;
}
public String reverseString(String inputStr) throws NullPointerException
{
return cool.reverseString(inputStr);
}

}
這個類也很簡單,相當於一個adapter。
下面看看如何使用easymock進行接口的打樁:
/************************************************************
* Project Name: lhjTest
* File Name : UseCoolInterfaceJTest.java
* File Desc : UseCoolInterfaceJTest.java
* Author : Administrator
* Create : 2007-3-25
* Modify:
***********************************************************/
package org.lhj.cool.mock;

import junit.framework.TestCase;

import org.easymock.MockControl;
import org.lhj.cool.junit.CoolInterface;
import org.lhj.cool.mock.UseCoolInterface;

/**
* @author Administrator
*/
public class UseCoolInterfaceJTest extends TestCase
{
private UseCoolInterface testInterface;
private MockControl ctrl;
private CoolInterface cool;

/* (non-Javadoc)
* @see junit.framework.TestCase#setUp()
*/
protected void setUp() throws Exception
{
super.setUp();
ctrl = MockControl.createControl(CoolInterface.class);
cool = (CoolInterface) ctrl.getMock();
testInterface = new UseCoolInterface();
testInterface.setCoolInterface(cool);
}

/* (non-Javadoc)
* @see junit.framework.TestCase#tearDown()
*/
protected void tearDown() throws Exception
{
super.tearDown();
}

/**
* Test method for {@link org.lhj.cool.mock.UseCoolInterface#reverseString(java.lang.String)}.
*/
public void testReverseString()
{
ctrl.expectAndThrow(cool.reverseString(null), new NullPointerException());
ctrl.expectAndReturn(cool.reverseString(""), null);
ctrl.expectAndReturn(cool.reverseString("AAaa"), "aaAA");
ctrl.expectAndReturn(cool.reverseString("aaAA"), "AAaa");
ctrl.replay();
try
{
testInterface.reverseString(null);
fail();
}
catch (NullPointerException e)
{
//donothing
}
assertNull(testInterface.reverseString(""));
assertEquals("aaAA", testInterface.reverseString("AAaa"));
assertEquals("AAaa", testInterface.reverseString("aaAA"));
ctrl.verify();
}
}
從上面可以看出使用easymock的步驟:
1、在setUp時創建MockControl,它是控制mock object行為的類,參數傳入欲打樁的接口的class。
2、從mockControl中getmock,返回mock object轉化為本類型對象。
3、創建欲測試類並將mock object傳進去。
以上幾步代碼為:

ctrl = MockControl.createControl(CoolInterface.class);

cool = (CoolInterface) ctrl.getMock();

testInterface = new UseCoolInterface();

testInterface.setCoolInterface(cool);
4、使用mockControl對mock object進行設置,這一步又稱為訓練,代碼為

ctrl.expectAndThrow(cool.reverseString(null), new NullPointerException());

ctrl.expectAndReturn(cool.reverseString(""), null);

ctrl.expectAndReturn(cool.reverseString("AAaa"), "aaAA");

ctrl.expectAndReturn(cool.reverseString("aaAA"), "AAaa");
第一行意思是告訴mockControl,mock object的reverseString方法會被調用,傳入的參數是null,並會拋出異常;第二行意思是告訴mockControl,mock object的reverseString方法會被調用,傳入的參數是"",並返回null;其他行類似。
5、訓練完之后可以接受檢查了,代碼為:

ctrl.replay();
6、現在可以進行測試了,以下是測試代碼:
可以看出,這跟一般的JUnit語句是一樣的。
7、檢驗測試結果

ctrl.verify();
mockControl會驗證mockObject是否按照訓練的情況參與了測試,即訓練時的方法是否被調用了,傳入的參數對不對。
以上示例只是easymock的一部分功能,除此之外,easymock還能測試mockObject的方法是否按照順序調用了,調用的次數對不對,傳入的參數是否在給定的范圍之內,另外還可以改變參數對象的內容。
除了easymock可以mock接口外,像mock objects,Jmock,mock Creator都可以實現此類功能。其中mock Creator還可以mock class,但是可惜mock creator是靜態打樁的,實現起來有額外的工作。我在公司里用的是jdk1.4,無法用easymockclassextension1.2,所以使用了easymock加mock creator,幸好mock creator有eclipse插件,生成mock class也比較方便。所以如果你使用的是JDK1。4,用easymock加mock creator絕對是最佳組合;如果你使用的是JDK1.5,那么使用easymock加easymockclassextension會使你工作更輕松。
來自:http://blog.csdn.net/hardblackking/article/details/1544519