easymock教程-运行时返回值或者异常
来至:http://skydream.iteye.com/blog/834158
前面的教程中,我们看到easymock可以通过expect方法来设定mock方法的返回值或者异常,但是注意这些案例中设置的返回值都是在调用被测试 的类的方法前就已经确定下来的,即我们其实在测试类的代码运行前(实际是在EasyMock.replay()方法调用前)就已经"预知"了返回结果。
但是在某些情况下,我们可能无法预知返回值,比如我们需要根据输入的参数值来决定返回什么,而这个参数可能无法在record阶段获得。因此在mock方法中我们无法在record阶段就决定应该返回什么。
对于这种场景,easymock提供了IAnswer接口和andAnswer()方法来提供运行时决定返回值或者异常的机制。
我们来看一个简单的例子:
private Service service;
public void execute() {
int count = ramdonInt();
int result = service.execute(count);
}
public void setService(Service service) {
this .service = service;
}
private int ramdonInt() {
Random random = new Random();
return random.nextInt() / 10000 ;
}
}
public interface Service {
public int execute( int count);
}
在Business的execute()方法中,需要调用service.execute(int count)方法,而传入的参数count是需要运行时才能确定的,这里为了简单我们random了一个int来模拟这种情况。
然后看测试案例
public void testRuntimeReturn() {
Business business = new Business();
Service service = EasyMock.createMock(Service. class );
business.setService(service);
EasyMock.expect(service.execute(EasyMock.anyInt())).andAnswer( new IAnswer < Integer > () {
public Integer answer() throws Throwable {
Integer count = (Integer) EasyMock.getCurrentArguments()[ 0 ];
return count * 2 ;
}
} );
EasyMock.replay(service);
business.execute();
EasyMock.verify(service);
}
这里我们通过EasyMock.expect(service.execute(EasyMock.anyInt()))来接受任意值的count参数输 入,andAnswer(new IAnswer<Integer>() {}) 让我们可以指定一个IAnswer的实现类来给出返回值。在这个IAnswer的实现类中,我们通过 EasyMock.getCurrentArguments()[0]获取到service.execute()方法的第一个参数,然后简单的运用 count*2规则给出返回值。这里的EasyMock.getCurrentArguments()方法可以获取到运行时的参数列表,不过注意这个方法 对重构不够友好,如果参数列表发生变化则必须手工修改对象的获取参数的代码。
下面是一个运行时抛出异常的例子,简单起见我们通过设置exception的message来在错误信息中传递运行时的count值。
public void testRuntimeException() {
Business business = new Business();
Service service = EasyMock.createMock(Service. class );
business.setService(service);
EasyMock.expect(service.execute(EasyMock.anyInt())).andAnswer( new IAnswer < Integer > () {
public Integer answer() throws Throwable {
Integer count = (Integer) EasyMock.getCurrentArguments()[ 0 ];
throw new RuntimeException( " count= " + count);
}
} );
EasyMock.replay(service);
try {
business.execute();
fail( " should throw RuntimeException " );
} catch (RuntimeException e) {
assertTrue(e.getMessage().indexOf( " count= " ) != - 1 );
// get count from message
EasyMock.verify(service);
}
}
除了IAnswer接口外,easymock中还有另外一个方式可以完成类似的功能,就是使用andDelegate()方法,
public int execute( int count) {
return count * 2 ;
}
}
@Test
public void testRuntimeReturn() {
Business business = new Business();
Service service = EasyMock.createMock(Service. class );
business.setService(service);
EasyMock.expect(service.execute(EasyMock.anyInt())).andDelegateTo( new ServiceStub());
EasyMock.replay(service);
business.execute();
EasyMock.verify(service);
}
这里需要先创建一个Service类的实现类和一个实例,然后通过andDelegateTo()将这个stub的实例传进去,注意这里delegate进去的实例必须是mock对象接口相同。
delegateTo方式实际上是我们手工创建了stub(mock和stub的概念及差别请参考本教程的"mock和stub"一文),这和我们使用 easymock的初衷有所违背。而且当这个接口有众多方法时,创建这样一个stub会显得很痛苦,不如使用IAnswer方便直接。