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











context 對(duì)象便可以用來(lái)創(chuàng)建Mock對(duì)象。
接下來(lái)的例子,我們模擬一個(gè)ServiceCall對(duì)象,我們以它的Map call(String target, Map dataMap)為例,針對(duì)此方法,設(shè)定預(yù)期值。然后我們?cè)趫?zhí)行用例的時(shí)候調(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)用時(shí),返回null
one(sCall).call(JMockService.queryDtlInfo, new HashMap());
will(returnValue(null));
}
});

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

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

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

校驗(yàn)expectations中的規(guī)則








































使用jMock時(shí),一般會(huì)通過(guò)如下代碼指定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)用時(shí),返回null
one(sCall).call(JMockService.queryDtlInfo, new HashMap());
will(returnValue(null));
}
});
![]()
.
}
}
為了校驗(yàn)expectations中的規(guī)則是否都滿足,可以在測(cè)試完成后通過(guò)增加 context.assertIsSatisfied()方法來(lái)驗(yàn)證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)用時(shí),返回null
one(sCall).call(JMockService.queryDtlInfo, new HashMap());
will(returnValue(null));
}
});
BookList bListAction = new BookList();
bListAction.setName("jnbzwm");
// 設(shè)定ServiceCall對(duì)象為Mock對(duì)象
bListAction.setUpfServiceCall(sCall);
// 執(zhí)行Action方法
bListAction.execute();
Assert.assertEquals("9800000000", bListAction.getOrderId());
Assert.assertEquals(0, bListAction.getDataList().size());
context.assertIsSatisfied();
}
由于我定義了兩條規(guī)則,而第二條并未調(diào)用,所以此用例不會(huì)通過(guò)。
同一個(gè)方法連續(xù)調(diào)用時(shí)返回不同的值
有兩種方法,第一種就是直接通過(guò)多次調(diào)用 will(returnValue(X))來(lái)指定。如:
@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)用時(shí),返回1
one(sCall).call(JMockService.queryDtlInfo, null);
will(returnValue(1));
// 第三次被調(diào)用時(shí),返回2
one(sCall).call(JMockService.queryDtlInfo, null);
will(returnValue(2));
}
});
![]()
}
然而第一種方法會(huì)增加維護(hù)成本,且缺乏可控性。jMock提供了第二種方法,即通過(guò)onConsecutiveCalls的action來(lái)實(shí)現(xiàn)返回不同的返回值。如:














指定mock的方法拋出異常
在will方法中直接使用throwException的action。參考如下語(yǔ)法:
one(sCall).call(JMockService.queryDtlInfo, null); // 設(shè)定預(yù)期值,拋出異常 will(throwException(new BusinessException("~", "name can't empty.")));
結(jié)合測(cè)試異常一起使用,代碼如下:
@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)用時(shí),返回null
one(sCall).call(JMockService.queryDtlInfo, new HashMap());
will(returnValue(null));
}
});
BookList bListAction = new BookList();
bListAction.setName("");
// 設(shè)定ServiceCall對(duì)象為Mock對(duì)象
bListAction.setUpfServiceCall(sCall);
// 執(zhí)行Action方法
bListAction.execute();
}