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();
}