jMock用法簡介
總體上來說,jMock 是一個輕量級的模擬對象技術(shù)的實現(xiàn)。它具有以下特點:
1.可以用簡單易行的方法定義模擬對象,無需破壞本來的代碼結(jié)構(gòu)表;
2.可以定義對象之間的交互,從而增強測試的穩(wěn)定性;
3.可以集成到測試框架;
4.易擴充;
使用 jMock 模擬對象
我們首先必須引入 jMock 的類,定義我們的測試類,創(chuàng)建一個 Mockery 的對象用來代表上下文。上下文可以模擬出對象和對象的輸出,并且還可以檢測應(yīng)用是否合法。











context 對象便可以用來創(chuàng)建Mock對象。
接下來的例子,我們模擬一個ServiceCall對象,我們以它的Map call(String target, Map dataMap)為例,針對此方法,設(shè)定預(yù)期值。然后我們在執(zhí)行用例的時候調(diào)用此方法,便可以得到預(yù)期值。
@SuppressWarnings("unchecked")
public class BookListTest {
private final Mockery context = new JUnit4Mockery() {
{
setImposteriser(ClassImposteriser.INSTANCE);
}
};

@Test
public void testExecuteNormal() throws Exception {
final ServiceCall sCall = context.mock(ServiceCall.class);
context.checking(new Expectations() {
{
one(sCall).call(JMockService.queryDtlInfo, null);
// 構(gòu)建預(yù)期結(jié)果
Map ret = new HashMap();
ret.put("OrderId", "9800000000");
ret.put("Data", new ArrayList());
// 設(shè)定預(yù)期值
will(returnValue(ret));
// 第二次被調(diào)用時,返回null
one(sCall).call(JMockService.queryDtlInfo, new HashMap());
will(returnValue(null));
}
});

BookList bListAction = new BookList();
bListAction.setName("jnbzwm");
// 設(shè)定ServiceCall對象為Mock對象
bListAction.setServiceCall(sCall);

// 執(zhí)行Action方法
bListAction.execute();

Assert.assertEquals("9800000000", bListAction.getOrderId());
Assert.assertEquals(0, bListAction.getDataList().size());
}

校驗expectations中的規(guī)則








































使用jMock時,一般會通過如下代碼指定expectations:
private final Mockery context = new JUnit4Mockery() {
{
setImposteriser(ClassImposteriser.INSTANCE);
}
};
@Test
public void testExecuteNormal() throws Exception {
final ServiceCall sCall = context.mock(ServiceCall.class);
context.checking(new Expectations() {
{
one(sCall).call(JMockService.queryDtlInfo, null);
// 構(gòu)建預(yù)期結(jié)果
Map ret = new HashMap();
ret.put("OrderId", "9800000000");
ret.put("Data", new ArrayList());
// 設(shè)定預(yù)期值
will(returnValue(ret));
![]()
// 第二次被調(diào)用時,返回null
one(sCall).call(JMockService.queryDtlInfo, new HashMap());
will(returnValue(null));
}
});
![]()
.
}
}
為了校驗expectations中的規(guī)則是否都滿足,可以在測試完成后通過增加 context.assertIsSatisfied()方法來驗證expectations是否滿足。
如下代碼:
@Test
public void testExecuteNormal() throws Exception {
final ServiceCall sCall = context.mock(ServiceCall.class);
context.checking(new Expectations() {
{
one(sCall).call(JMockService.queryDtlInfo, null);
// 構(gòu)建預(yù)期結(jié)果
Map ret = new HashMap();
ret.put("OrderId", "9800000000");
ret.put("Data", new ArrayList());
// 設(shè)定預(yù)期值
will(returnValue(ret));
![]()
// 第二次被調(diào)用時,返回null
one(sCall).call(JMockService.queryDtlInfo, new HashMap());
will(returnValue(null));
}
});
BookList bListAction = new BookList();
bListAction.setName("jnbzwm");
// 設(shè)定ServiceCall對象為Mock對象
bListAction.setUpfServiceCall(sCall);
// 執(zhí)行Action方法
bListAction.execute();
Assert.assertEquals("9800000000", bListAction.getOrderId());
Assert.assertEquals(0, bListAction.getDataList().size());
context.assertIsSatisfied();
}
由于我定義了兩條規(guī)則,而第二條并未調(diào)用,所以此用例不會通過。
同一個方法連續(xù)調(diào)用時返回不同的值
有兩種方法,第一種就是直接通過多次調(diào)用 will(returnValue(X))來指定。如:
@Test
public void testExecuteNormal() throws Exception {
final ServiceCall sCall = context.mock(ServiceCall.class);
context.checking(new Expectations() {
{
one(sCall).call(JMockService.queryDtlInfo, null);
will(returnValue(0));
// 第二次被調(diào)用時,返回1
one(sCall).call(JMockService.queryDtlInfo, null);
will(returnValue(1));
// 第三次被調(diào)用時,返回2
one(sCall).call(JMockService.queryDtlInfo, null);
will(returnValue(2));
}
});
![]()
}
然而第一種方法會增加維護成本,且缺乏可控性。jMock提供了第二種方法,即通過onConsecutiveCalls的action來實現(xiàn)返回不同的返回值。如:














指定mock的方法拋出異常
在will方法中直接使用throwException的action。參考如下語法:
one(sCall).call(JMockService.queryDtlInfo, null); // 設(shè)定預(yù)期值,拋出異常 will(throwException(new BusinessException("~", "name can't empty.")));
結(jié)合測試異常一起使用,代碼如下:
@Test(expected=BusinessException.class)
public void testExecuteNormal() throws Exception {
final ServiceCall sCall = context.mock(ServiceCall.class);
context.checking(new Expectations() {
{
one(sCall).call(JMockService.queryDtlInfo, null);
// 構(gòu)建預(yù)期結(jié)果
Map ret = new HashMap();
ret.put("OrderId", "9800000000");
ret.put("Data", new ArrayList());
// 設(shè)定預(yù)期值
will(throwException(new BusinessException("~", "name can't empty.")));
// 第二次被調(diào)用時,返回null
one(sCall).call(JMockService.queryDtlInfo, new HashMap());
will(returnValue(null));
}
});
BookList bListAction = new BookList();
bListAction.setName("");
// 設(shè)定ServiceCall對象為Mock對象
bListAction.setUpfServiceCall(sCall);
// 執(zhí)行Action方法
bListAction.execute();
}