Java 如何模拟 void 方法以引发异常?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/27866181/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I mock a void method to throw an exception?
提问by Luiggi Mendoza
I have a structure like this:
我有这样的结构:
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;
}
}
I'm creating unit tests for SomeClient#getEntity
methodand 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.
我正在为SomeClient#getEntity
方法创建单元测试,并且必须涵盖所有场景。例如,我需要涵盖cacheWrapper
. 我遵循的方法是为CacheWrapper
类创建一个模拟,使CacheWrapper
类上的方法抛出 a RuntimeException
,在SomeClient
和 test的实例中设置这个模拟Someclient#getEntity
。问题是在尝试模拟putInSharedMemory
方法时,因为 is void
。我尝试了很多方法来做到这一点,但都没有奏效。该项目依赖于 PowerMock 和 EasyMock。
Here are my attempts:
这是我的尝试:
- 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: Unexpected method call putInSharedMemory("foo", com.company.domain.Entity@609fc98)
Added Mockito dependency to the projectto 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 cannot be cast to org.powermock.api.mockito.internal.invocationcontrol.MockitoMethodInvocationControl
- 使用
EasyMock.<Void>expect
. 这引发了编译器错误。 试图存根
CacheWrapper#putInSharedMemory
。没有工作,因为引发了此错误消息的异常:java.lang.AssertionError: 意外的方法调用 putInSharedMemory("foo", com.company.domain.Entity@609fc98)
向项目添加了 Mockito 依赖项以利用
PowerMockito
类的功能。但这引发了一个异常,因为它没有与 EasyMock 集成。这是引发的异常:java.lang.ClassCastException: org.powermock.api.easymock.internal.invocationcontrol.EasyMockMethodInvocationControl 无法转换为 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;
}
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
.
我无法更改 的实现,CacheWrapper
因为它来自第三方库。此外,我无法使用,EasyMock#getLastCall
因为我正在对SomeClient#getEntity
.
How can I overcome this?
我怎样才能克服这个问题?
采纳答案by fge
Since none of your classes are final, you can use "pure mockito" without resorting to PowerMockito:
由于您的课程都不是最终课程,因此您可以使用“纯模拟”而无需求助于 PowerMockito:
final CacheWrapper wrapper = Mockito.spy(new CacheWrapper());
Mockito.doThrow(something)
.when(wrapper).putInSharedMemory(Matchers.any(), Matchers.any());
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.
请注意,存根的“方法参数”实际上是参数匹配器;您可以放置特定值(如果没有被特定方法“包围”,它将调用.equals()
)。因此,您可以针对不同的参数以不同的方式引导存根的行为。
Also, no need for any kind of .replay()
with Mockito, which is very nice!
此外,不需要任何类型的.replay()
Mockito,这非常好!
Finally, be aware that you can doCallRealMethod()
as well. After that, it depends on your scenarios...
最后,请注意,您也可以doCallRealMethod()
。之后,这取决于你的场景......
(note: last mockito version available on maven is 1.10.17 FWIW)
(注意:maven 上可用的最后一个 mockito 版本是 1.10.17 FWIW)
回答by NiematojakTomasz
Use expectLastCall
, like:
使用expectLastCall
,例如:
cacheWrapper.putInSharedMemory(EasyMock.anyObject(), EasyMock.anyObject())
EasyMock.expectLastCall().andThrow(new RuntimeException("This is an intentional Exception")).anyTimes();
回答by nhylated
Are you using EasyMock or Mockito? Both are different frameworks.
您使用的是 EasyMock 还是 Mockito?两者都是不同的框架。
PowerMockito is a superset (or more of a supplement) that can be used with both these frameworks. PowerMockito allows you to do things that Mockito or EasyMock don't.
PowerMockito 是一个超集(或更多的补充),可以与这两个框架一起使用。PowerMockito 允许您做 Mockito 或 EasyMock 不做的事情。
Try this for stubbing void methods to throw exceptions:
试试这个来存根 void 方法来抛出异常:
EasyMock:
EasyMock:
// First make the actual call to the void method.
cacheWrapper.putInSharedMemory("key", "value");
EasyMock.expectLastCall().andThrow(new RuntimeException());
Check:
查看:
- http://easymock.org/api/org/easymock/internal/MocksControl.html#andVoid--
- Getting EasyMock mock objects to throw Exceptions
Mockito:
莫基托:
// Create a CacheWrapper spy and stub its method to throw an exception.
// Syntax for stubbing a spy's method is different from stubbing a mock's method (check Mockito's docs).
CacheWrapper spyCw = spy(new CacheWrapper());
Mockito.doThrow(new RuntimeException())
.when(spyCw)
.putInSharedMemory(Matchers.any(), Matchers.any());
SomeClient sc = new SomeClient();
sc.setCacheWrapper(spyCw);
// This will call spyCw#putInSharedMemory that will throw an exception.
sc.getEntity("key");