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

public class BookListTest
{

private final Mockery context = new JUnit4Mockery()
{

{
// 聲明針對類進行mock,針對接口則會采用動態代理,不需要聲明
setImposteriser(ClassImposteriser.INSTANCE);
}
};
}


context 對象便可以用來創建Mock對象。
接下來的例子,我們模擬一個ServiceCall對象,我們以它的Map call(String target, Map dataMap)為例,針對此方法,設定預期值。然后我們在執行用例的時候調用此方法,便可以得到預期值。
@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);
// 構建預期結果
Map ret = new HashMap();
ret.put("OrderId", "9800000000");
ret.put("Data", new ArrayList());
// 設定預期值
will(returnValue(ret));
// 第二次被調用時,返回null
one(sCall).call(JMockService.queryDtlInfo, new HashMap());
will(returnValue(null));
}
});

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

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

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


校驗expectations中的規則
使用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);
// 構建預期結果
Map ret = new HashMap();
ret.put("OrderId", "9800000000");
ret.put("Data", new ArrayList());
// 設定預期值
will(returnValue(ret));
// 第二次被調用時,返回null
one(sCall).call(JMockService.queryDtlInfo, new HashMap());
will(returnValue(null));
}
});
.
}
}

為了校驗expectations中的規則是否都滿足,可以在測試完成后通過增加 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);
// 構建預期結果
Map ret = new HashMap();
ret.put("OrderId", "9800000000");
ret.put("Data", new ArrayList());
// 設定預期值
will(returnValue(ret));
// 第二次被調用時,返回null
one(sCall).call(JMockService.queryDtlInfo, new HashMap());
will(returnValue(null));
}
});

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

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

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

由于我定義了兩條規則,而第二條并未調用,所以此用例不會通過。
同一個方法連續調用時返回不同的值
有兩種方法,第一種就是直接通過多次調用 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));

// 第二次被調用時,返回1
one(sCall).call(JMockService.queryDtlInfo, null);
will(returnValue(1));

// 第三次被調用時,返回2
one(sCall).call(JMockService.queryDtlInfo, null);
will(returnValue(2));
}
});

}

然而第一種方法會增加維護成本,且缺乏可控性。jMock提供了第二種方法,即通過onConsecutiveCalls的action來實現返回不同的返回值。如:
@Test

public void testExecuteNormal() throws Exception
{
final ServiceCall sCall = context.mock(ServiceCall.class);

context.checking(new Expectations()
{

{
atLeast(1).of (sCall).call(JMockService.queryDtlInfo, null);
will(onConsecutiveCalls( returnValue(0), returnValue(1), returnValue(2)));
}
});

}


指定mock的方法拋出異常
在will方法中直接使用throwException的action。參考如下語法:
one(sCall).call(JMockService.queryDtlInfo, null);
// 設定預期值,拋出異常
will(throwException(new BusinessException("~", "name can't empty.")));
結合測試異常一起使用,代碼如下:
@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);
// 構建預期結果
Map ret = new HashMap();
ret.put("OrderId", "9800000000");
ret.put("Data", new ArrayList());
// 設定預期值
will(throwException(new BusinessException("~", "name can't empty.")));

// 第二次被調用時,返回null
one(sCall).call(JMockService.queryDtlInfo, new HashMap());
will(returnValue(null));
}
});

BookList bListAction = new BookList();
bListAction.setName("");
// 設定ServiceCall對象為Mock對象
bListAction.setUpfServiceCall(sCall);

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

