java 如何处理 org.mockito.exceptions.misusing.MissingMethodInvocationException?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/26609499/
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 to deal with org.mockito.exceptions.misusing.MissingMethodInvocationException?
提问by daydreamer
I have a class that looks like
我有一个看起来像的课程
@Stateless
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public class ClientRegistrationManager {
private CrudService crudService;
private UniqueIdGenerator uniqueIdGenerator;
@SuppressWarnings("UnusedDeclaration")
public ClientRegistrationManager() {
}
@Inject
public ClientRegistrationManager(@Nonnull final CrudService crudService, @Nonnull final UniqueIdGenerator uniqueIdGenerator) {
this.crudService = crudService;
this.uniqueIdGenerator = uniqueIdGenerator;
}
public Member register(@Nonnull final String email, @Nonnull final String userExternalId, @Nonnull final String password) {
final Member existingMember;
try {
existingMember = getMemberQueries().getMemberByEmail(email);
} catch (final NoResultException e) {
return createNewMemberAndGetClientDetail(email, userExternalId);
}
return existingMember;
}
...
@Nonnull
protected MemberQueries getMemberQueries() {
return new MemberQueries(crudService);
}
}
I wanted to test it by mocking out any external behavior, so I created getMemberQueries()
using Pattern - 1 described in doc
我想通过模拟任何外部行为来测试它,所以我getMemberQueries()
使用文档中描述的 Pattern - 1创建
I, then write my test as following
我,然后写我的测试如下
public class ClientRegistrationManagerTest {
@Mock
private MemberQueries memberQueries;
@Mock
private CrudService crudService;
@Mock
private UniqueIdGenerator uniqueIdGenerator;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
@Test
public void testMockedMemberQueries() {
final ClientRegistrationManager clientRegistrationManager = new ClientRegistrationManager(crudService, uniqueIdGenerator);
Mockito.when(clientRegistrationManager.getMemberQueries()).thenReturn(memberQueries);
clientRegistrationManager.getMemberQueries();
assertTrue(true);
}
}
When I run this, I get the error as
当我运行这个时,我得到的错误是
org.mockito.exceptions.misusing.MissingMethodInvocationException:
when() requires an argument which has to be 'a method call on a mock'.
For example:
when(mock.getArticles()).thenReturn(articles);
Also, this error might show up because:
1. you stub either of: final/private/equals()/hashCode() methods.
Those methods *cannot* be stubbed/verified.
2. inside when() you don't call method on mock but on some other object.
3. the parent of the mocked class is not public.
It is a limitation of the mock engine.
Now what it means I need to mock out the entire ClientRegistrationManager
, but that the class I want to test, I can not just mock that entire class.
现在这意味着我需要模拟整个ClientRegistrationManager
,但是我想测试的类,我不能只是模拟整个类。
I am trying to understand my options here, I really do not want to depend on persistence
layer, that will be too heavy
我试图在这里了解我的选择,我真的不想依赖persistence
层,那太重了
采纳答案by Jeff Bowman
Seelenvirtuoseis correct in the comments: The root problem is that you can only call when
on mocks and spies, and that it is typically an anti-pattern to mock the class-under-test.
Seelenvirtuose在评论中是正确的:根本问题是您只能调用when
模拟和间谍,并且模拟被测类通常是一种反模式。
One technique is to use spy
, which lets you intercept and verify calls to a real object ("partial mocking"):
一种技术是使用spy
,它可以让您拦截和验证对真实对象的调用(“部分模拟”):
final ClientRegistrationManager clientRegistrationManager =
Mockito.spy(new ClientRegistrationManager(crudService, uniqueIdGenerator);
// doReturn is important because the call to contains a call to the mock
// before Mockito has intercepted it. In your case, this may just create a useless
// real MemberQueries, but in other cases it can throw an exception.
Mockito.doReturn(memberQueries).when(clientRegistrationManager).getMemberQueries();
As Predrag Magic wrote, another way to do this is to create a factory and replace that during the test. This is especially good if you pass in the factory as an optional constructor argument, because then production systems could create and pass in their own MemberQueryFactory and your class would still work as expected.
正如Predrag Magic 所写,另一种方法是创建一个工厂并在测试期间替换它。如果您将工厂作为可选的构造函数参数传入,这尤其好,因为这样生产系统可以创建并传入他们自己的 MemberQueryFactory 并且您的类仍然可以按预期工作。
public class ClientRegistrationManager {
static class MemberQueriesFactory {
MemberQueries getMemberQueries() {
return ObjectFactory.newMemberQueries(crudService);
}
}
/** Package-private. Visible for unit testing. */
MemberQueriesFactory memberQueriesFactory = new MembersQueryFactory();
}
@RunWith(MockitoJUnitRunner.class)
public class ClientRegistrationManagerTest {
@Mock ClientRegistrationManager.MembersQueryFactory mockMembersQueryFactory;
private ClientRegistrationManager getManagerForTest() {
ClientRegistrationManager manager = new ClientRegistrationManager();
manager.memberQueriesFactory = mockMembersQueryFactory;
return manager;
}
}
My favorite way, though, is to skip Mockito and directly override the class-under-test in the test:
不过,我最喜欢的方法是跳过 Mockito 并直接覆盖测试中的被测类:
@RunWith(MockitoJUnitRunner.class)
public class ClientRegistrationManagerTest {
@Mock MemberQueries memberQueries;
private ClientRegistrationManager getManagerForTest() {
ClientRegistrationManager manager = new ClientRegistrationManager() {
@Override void getMemberQueries() {
return memberQueries;
}
};
}
}
回答by Predrag Maric
One option is to use some kind of object factory to create new objects in classes under test, and then mock that factory.
一种选择是使用某种对象工厂在被测类中创建新对象,然后模拟该工厂。
protected MemberQueries getMemberQueries() {
return ObjectFactory.newMemberQueries(crudService);
}
If, for some reason, you want to mock just selected methods of some object instead of the whole object, you can do that with Mockito. Check out thisexample.
如果出于某种原因,您只想模拟某个对象的选定方法而不是整个对象,则可以使用 Mockito 来实现。看看这个例子。