如何模拟void方法抛出异常? [英] How can I mock a void method to throw an exception?
问题描述
我有这样的结构:
public class CacheWrapper {
private Map<Object, Object> innerMap;
public CacheWrapper() {
//initialize the innerMap with an instance for an in-memory cache
//that works on external server
//current implementation is not relevant for the problem
innerMap = ...;
}
public void putInSharedMemory(Object key, Object value) {
innerMap.put(key, value);
}
public Object getFromSharedMemory(Object key) {
return innerMap.get(key);
}
}
我的客户端类(你可以说它看起来像这个):
And my client class (you could say it looks like this):
public class SomeClient {
//Logger here, for exception handling
Logger log = ...;
private CacheWrapper cacheWrapper;
//getter and setter for cacheWrapper...
public Entity getEntity(String param) {
Entity someEntity = null;
try {
try {
entity = cacheWrapper.getFromSharedMemory(param);
} catch (Exception e) {
//probably connection failure occurred here
log.warn("There was a problem when getting from in-memory " + param + " key.", e);
}
if (entity == null) {
entity = ...; //retrieve it from database
//store in in-memory cache
try {
cacheWrapper.put(param, entity);
} catch (Exception e) {
//probably connection failure occurred here
log.warn("There was a problem when putting in in-memory " + param + " key.", e);
}
}
} catch (Exception e) {
logger.error(".......", e);
}
return entity;
}
}
我正在为<创建单元测试code> SomeClient#getEntity 方法,必须涵盖所有方案。例如,我需要介绍 cacheWrapper
引发异常的情况。我正在遵循的方法是为 CacheWrapper
类创建一个模拟,使 CacheWrapper
类上的方法抛出一个 RuntimeException
,在 SomeClient
的实例中设置此模拟并测试 Someclient #getEntity
。问题在于尝试模拟 putInSharedMemory
方法,因为 void
。我已经尝试了很多方法来做到这一点,但没有一个工作。 该项目依赖于PowerMock和EasyMock 。
I'm creating unit tests for SomeClient#getEntity
method and have to cover all scenarios. For instance, I need to cover the scenario where there are exceptions thrown by cacheWrapper
. The approach I'm following is to create a mock for CacheWrapper
class, make the methods on CacheWrapper
class to throw a RuntimeException
, set this mock in an instance of SomeClient
and test Someclient#getEntity
. The problem is when trying to mock putInSharedMemory
method because is void
. I have tried lot of ways to do this but none of them work. The project has dependencies for PowerMock and EasyMock.
以下是我的尝试:
- 使用
EasyMock。< Void>期望
。这引发了编译错误。 -
尝试存根
CacheWrapper#putInSharedMemory
。没有用,因为这个错误消息引发了异常:
- Using
EasyMock.<Void>expect
. This raised a compiler error. Tried to stub
CacheWrapper#putInSharedMemory
. Didn't worked because raised an exception with this error message:
java.lang.AssertionError:意外的方法调用putInSharedMemory(foo, com.company.domain.Entity@609fc98)
java.lang.AssertionError: Unexpected method call putInSharedMemory("foo", com.company.domain.Entity@609fc98)
为项目增加了Mockito依赖利用 PowerMockito
类的功能。但这引发了一个例外,因为它没有与EasyMock集成。这是引发的异常:
Added Mockito dependency to the project to make use of the functionality of PowerMockito
class. But this raised an exception because it doesn't integrate with EasyMock. This is the exception raised:
java.lang.ClassCastException:org.powermock.api.easymock.internal.invocationcontrol.EasyMockMethodInvocationControl无法强制转换to org.powermock.api.mockito.internal.invocationcontrol.MockitoMethodInvocationControl
java.lang.ClassCastException: org.powermock.api.easymock.internal.invocationcontrol.EasyMockMethodInvocationControl cannot be cast to org.powermock.api.mockito.internal.invocationcontrol.MockitoMethodInvocationControl
这里是此单元测试示例的代码:
Here's the code for this unit test sample:
@Test
public void getEntityWithCacheWrapperException() {
CacheWrapper cacheWrapper = mockThrowsException();
SomeClient someClient = new SomeClient();
someClient.setCacheWrapper(cacheWrapper);
Entity entity = someClient.getEntity();
//here.....................^
//cacheWrapper.putInSharedMemory should throw an exception
//start asserting here...
}
//...
public CacheWrapper mockThrowsException() {
CacheWrapper cacheWrapper = PowerMock.createMock(CacheWrapper.class);
//mocking getFromSharedMemory method
//this works like a charm
EasyMock.expect(cacheWrapper.getFromSharedMemory(EasyMock.anyObject()))
.andThrow(new RuntimeException("This is an intentional Exception")).anyTimes();
//mocking putInSharedMemory method
//the pieces of code here were not executed at the same time
//instead they were commented and choose one approach after another
//attempt 1: compiler exception: <Void> is not applicable for <void>
EasyMock.<Void>expect(cacheWrapper.putInSharedMemory(EasyMock.anyObject(), EasyMock.anyObject()))
.andThrow(new RuntimeException("This is an intentional Exception")).anyTimes();
//attempt 2: stubbing the method
//exception when executing the test:
//Unexpected method call putInSharedMemory("foo", com.company.domain.Entity@609fc98)
Method method = PowerMock.method(CacheWrapper.class, "putInSharedMemory", Object.class, Object.class);
PowerMock.stub(method).toThrow(new RuntimeException("Exception on purpose."));
//attempt 3: added dependency to Mockito integrated to PowerMock
//bad idea: the mock created by PowerMock.createMock() belongs to EasyMock, not to Mockito
//so it breaks when performing the when method
//exception:
//java.lang.ClassCastException: org.powermock.api.easymock.internal.invocationcontrol.EasyMockMethodInvocationControl
//cannot be cast to org.powermock.api.mockito.internal.invocationcontrol.MockitoMethodInvocationControl
//at org.powermock.api.mockito.internal.expectation.PowerMockitoStubberImpl.when(PowerMockitoStubberImpl.java:54)
PowerMockito.doThrow(new RuntimeException("Exception on purpose."))
.when(cacheWrapper).putInSharedMemory(EasyMock.anyObject(), EasyMock.anyObject());
PowerMock.replay(cacheWrapper);
return cacheWrapper;
}
我无法更改 CacheWrapper $的实现c $ c>因为它来自第三方库。另外,我不能使用
EasyMock#getLastCall
,因为我正在 SomeClient#getEntity
上执行测试。
I cannot change the implementation of CacheWrapper
because it comes from a third party library. Also, I cannot use EasyMock#getLastCall
because I'm performing the test on SomeClient#getEntity
.
我怎样才能克服这个问题?
How can I overcome this?
推荐答案
由于你的课程都不是最终的,您可以使用pure mockito而无需使用PowerMockito:
Since none of your classes are final, you can use "pure mockito" without resorting to PowerMockito:
final CacheWrapper wrapper = Mockito.spy(new CacheWrapper());
Mockito.doThrow(something)
.when(wrapper).putInSharedMemory(Matchers.any(), Matchers.any());
注意存根的方法参数实际上是参数匹配器;您可以设置特定值(如果没有被特定方法包围,它将调用 .equals()
)。因此,您可以针对不同的参数不同地指导存根的行为。
Note that "method arguments" to a stub are in fact argument matchers; you can put specific values (if not "surrounded" by a specific method it will make a call to .equals()
). So, you can guide the stub's behavior differently for different arguments.
此外,不需要任何类型的 .replay()
和Mockito,非常好!
Also, no need for any kind of .replay()
with Mockito, which is very nice!
最后,请注意你可以 doCallRealMethod()
as好。在那之后,这取决于你的场景...
Finally, be aware that you can doCallRealMethod()
as well. After that, it depends on your scenarios...
(注意:maven上的最后一个mockito版本是1.10.17 FWIW)
(note: last mockito version available on maven is 1.10.17 FWIW)
这篇关于如何模拟void方法抛出异常?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!