MockObjects的選擇:EasyMock與JMock的比較
本文假設讀者已經瞭解了MockObjects的使用目的和基本方式,不對MockTest之類的技術作過多解釋。僅提醒一句:“不要測試你的MockObjects”。
本文作爲一個評測結果的同時,也可以作爲EasyMock和jMock的簡短教程。他們本身都很易用,可惜帶的示例過於複雜,都用了過多的模式。看過本文的例子,相信就可以從容的在項目中使用了。
Java中常用的MockObjects有EasyMock和jMock等。其中EasyMock開發較早,已經出了1.1版本,而jMock前幾天纔剛推出了1.0 final。作爲剛成熟的小弟弟,jMock有什麼競爭實力呢?
本比較針對於以下幾個方面,代碼請見附件。
1 是否能夠對具體類進行模擬(當然,對接口模擬是基本功能)
2 是否能夠對方法名,參數,返回值進行動態控制
3 基本代碼行數
4 是否能夠對具有構造參數的具體類模擬
現在比較開始了。首先製作若干測試文件,很簡單。要模擬的有一個接口和一個具體類,叫做TheInterfaceToMock和TheClassToMock,另外,提供方法SampleReturn sampleMethod(Parameter p);以及同名無參數方法。
第一個測試是針對TheInterfaceToMock,提供ParameterImpl和SampleReturnImpl作爲期待的參數和返回值。
jMock代碼如下:
public class JMockUsage extends MockObjectTestCase { public void testReturnValueWithParemeter(){ // 構造Mock控制器 Mock m = new Mock(TheInterfaceToMock.class); // 這是要測試MockObject TheInterfaceToMock mock = (TheInterfaceToMock) m.proxy(); // 期待的返回值 SampleReturn sr = new SampleReturnImpl(); // 期待的參數 Parameter p = new ParameterImpl();
// 控制器,期待一次,方法sampleMethod,參數等於p(equals),將返回sr m.expects(once()).method("sampleMethod") .with(eq(p)).will(returnValue(sr));
// 正式執行mockobject SampleReturn ret = mock.sampleMethod(new ParameterImpl()); // 確定返回值是相同的 assertSame(sr,ret); }
} |
相同功能的easyMcok代碼如下:
public class EasyMockUsage extends TestCase { public void testReturnValueWithParameter(){ // 構造mock控制器 MockControl control = MockControl.createControl(TheInterfaceToMock.class); // 這是要測試的MockObject TheInterfaceToMock mock = (TheInterfaceToMock) control.getMock(); // 這是要返回的值 SampleReturn sr = new SampleReturnImpl(); // 這是要傳入的參數 Parameter p = new ParameterImpl();
// 恢復到記錄(record)狀態 control.reset(); // 首先記錄sampleMethod方法 mock.sampleMethod(p); // 設定該方法的返回值 control.setReturnValue(sr); // 切換狀態爲回覆(reply) control.replay(); // 正式執行mock object的方法,明顯的,參數值是equals而不是same SampleReturn ret = mock.sampleMethod(new ParameterImpl());
// 確定返回值是需要的值 assertSame(sr,ret); } |
從上面的代碼可以看到,同樣的功能,二者的行數相差3行。其主要原因,就是easyMcok的Mock機制是基於狀態,首先是錄製狀態,記錄下來待測的方法和參數,返回值等,然後切換爲回覆狀態。而jMock沒有切換這一步,直接將參數返回值用一句話寫出來。確實是一句話:期待一次,方法sampleMethod,參數等於p(equals),將返回sr。其中的一些輔助函數,例如returnValue,eq等等,位於父類MockTestCase。
結論:
1 如果不能提供MockTestCase作爲父類,請使用EasyMock 2 如果需要批量或動態生成測試,請使用更規則的jMock 3 如果喜歡看起來行數少一些,請用jMock 4 如果對狀態切換看不順眼,請用Mock
|
下面進行具體類測試,一個共同的點是,二者均使用了CGLIB作爲增強器,因此效率差別幾乎沒有。將上面的測試稍稍修改,將TheInterfaceToMock改爲TheClassToMock。發生了以下變化。
用jMock,需要將import替換爲新的import,代碼中其他部分完全不變!
原來 import org.jmock.Mock; import org.jmock.MockObjectTestCase; 改爲: import org.jmock.cglib.Mock; import org.jmock.cglib.MockObjectTestCase;
|
這是個相當體貼的設計,保證了接口的一致性。對於一套API來說,同樣的類卻有不同的使用方法是個噩夢。
用easyMock,需要新增加一個import。並且修改一些聲明的地方。
原來 import org.easymock.MockControl; 增加 import org.easymock.classextension.MockClassControl; |
// mock控制器 MockControl control = MockClassControl.createControl(TheClassToMock.class);
|
其他部分不需要變化。雖然這有些變化,但是變化帶來了其他的好處,就是:能夠支持帶有構造參數的具體類,而jMock不支持。這對於大量使用了PicoContainer的代碼來說不啻是一個福音。
結論:
5 如果需要構造參數,只能使用easyMock 6 如果喜歡用相同的API操作並且不在乎構造參數,請用jMock 7 如果願意等待下一版本的jMock提供構造參數支持,請用jMock
|
參考比較表:
| EasyMock | jMock |
通過接口模擬 | 是 | 是 |
控制方法有效次數 | 是 | 是 |
定製參數匹配 | 是 | 是 |
不需要狀態轉換 | 否 | 是 |
具體類模擬 | 是 | 是 |
具體類可有構造參數 | 是 | 否 |
接口統一 | 否 | 是 |
條件代碼在一行中完成 | 否 | 是 |
支持其他參數規則,如not | 否 | 是 |
自驗證 verify() | 是 | 是 |
綜上,我選擇了jMock。不過想想看,easyMock用3個類實現了大多數常用功能,很不簡單啊。而jMock,如果能夠提供對Constructor injection的支持就完美了。遺憾。不過從設計上看,jMock裏的模式使用堪稱典範,很好看哦。
本人對easyMock使用經驗不多,如有謬誤請指出。
下載地址:
比較代碼:
http://icecloud.51.net/data/mockobjects.zip
需要:
JUnit3.8.1,Cglib2,jMock1.0,EasyMock1.1
|