Java 如何处理org.mockito.exceptions.misusing.MissingMethodInvocationException?
我有一门课看起来像Java 如何处理org.mockito.exceptions.misusing.MissingMethodInvocationException?,java,unit-testing,mocking,mockito,Java,Unit Testing,Mocking,Mockito,我有一门课看起来像 @Stateless @TransactionAttribute(TransactionAttributeType.REQUIRED) public class ClientRegistrationManager { private CrudService crudService; private UniqueIdGenerator uniqueIdGenerator; @SuppressWarnings("UnusedDeclaration")
@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);
}
}
我想通过模拟任何外部行为来测试它,所以我使用中描述的模式-1创建了getMemberQueries()
一、 然后按照下面的步骤编写我的测试
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);
}
}
当我运行这个时,我得到的错误是
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.
这意味着我需要模拟整个ClientRegistrationManager
,但是我要测试的类,我不能只是模拟整个类
我试图理解我在这里的选择,我真的不想依赖于
持久性
层,这将太重一个选择是使用某种对象工厂在测试的类中创建新对象,然后模拟该工厂
protected MemberQueries getMemberQueries() {
return ObjectFactory.newMemberQueries(crudService);
}
如果出于某种原因,您只想模拟某个对象的选定方法,而不是整个对象,那么可以使用Mockito进行模拟。查看示例。在注释中是正确的:根本问题是,您只能在模拟和间谍时调用,并且模拟被测试的类通常是一种反模式
一种技术是使用spy
,它允许您拦截和验证对真实对象的调用(“部分模拟”):
同样,另一种方法是创建一个工厂,并在测试期间替换它。如果您将工厂作为可选构造函数参数传入,这尤其好,因为这样生产系统就可以创建并传入自己的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;
}
}
不过,我最喜欢的方法是跳过Mockito,直接在测试中重写被测试的类:
@RunWith(MockitoJUnitRunner.class)
public class ClientRegistrationManagerTest {
@Mock MemberQueries memberQueries;
private ClientRegistrationManager getManagerForTest() {
ClientRegistrationManager manager = new ClientRegistrationManager() {
@Override void getMemberQueries() {
return memberQueries;
}
};
}
}
好的,您自己正在创建一个ClientRegistrationManager
的实例,因此它不是mock,但在时将其传递给。我认为,这条信息非常清楚:“when()需要一个参数,该参数必须是‘模拟的方法调用’”。应该只为被测试类的依赖项创建mock。是的@Seelenvirtuose,我理解这一点。我正在寻找如何模拟在测试类内部创建的新对象()
的想法这归结为一个问题:为什么要模拟测试类?我不想这样做,我想模拟测试类的新对象()
创建。。。我不明白。也许更大的图景在这里是必要的。你能再描述一下吗?事实上,永远不应该有必要模拟被测试的类,也不应该有必要对被测试类的模拟实例的方法调用设置结果(就像您所做的那样)。我喜欢spy的想法,因为它们不太侵入性,而且我不必编写太多代码来测试
@RunWith(MockitoJUnitRunner.class)
public class ClientRegistrationManagerTest {
@Mock MemberQueries memberQueries;
private ClientRegistrationManager getManagerForTest() {
ClientRegistrationManager manager = new ClientRegistrationManager() {
@Override void getMemberQueries() {
return memberQueries;
}
};
}
}