easymock教程-运行时返回值或者异常??????? 前面的教程中,我们看到easymock可以通过expect方法来设定mock
easymock教程-运行时返回值或者异常
??????? 前面的教程中,我们看到easymock可以通过expect方法来设定mock方法的返回值或者异常,但是注意这些案例中设置的返回值都是在调用被测试的类的方法前就已经确定下来的,即我们其实在测试类的代码运行前(实际是在EasyMock.replay()方法调用前)就已经"预知"了返回结果。
?
??? 但是在某些情况下,我们可能无法预知返回值,比如我们需要根据输入的参数值来决定返回什么,而这个参数可能无法在record阶段获得。因此在mock方法中我们无法在record阶段就决定应该返回什么。
?
??? 对于这种场景,easymock提供了IAnswer接口和andAnswer()方法来提供运行时决定返回值或者异常的机制。
?
??? 我们来看一个简单的例子:
????public?class?Business?{
????????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来模拟这种情况。
?
??? 然后看测试案例
????@Test
????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值。
???@Test
????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?class?ServiceStub?implements?Service?{
????????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方便直接。
?