我有一个类,看起来像
@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);
}
}
我想通过模拟任何外部行为来测试它,所以我使用doc中描述的Pattern -1创建了getMemberQueries()
I,然后按如下方式编写我的测试
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
,但是我想测试的类,我不能只是模拟整个类。
我试着在这里理解我的选择,我真的不想依赖于persistence
层,那会太重了
发布于 2014-10-28 23:33:12
Seelenvirtuose在评论中是正确的:根本问题是您只能在模拟和间谍上调用when
,并且它通常是模拟被测类的反模式。
一种技术是使用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();
作为Predrag Magic wrote,另一种方法是创建一个工厂并在测试期间替换它。如果您将工厂作为一个可选的构造函数参数传入,这一点尤其有用,因为这样生产系统就可以创建并传入它们自己的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;
}
};
}
}
发布于 2014-10-28 21:33:41
一种选择是使用某种对象工厂在测试类中创建新对象,然后模拟该工厂。
protected MemberQueries getMemberQueries() {
return ObjectFactory.newMemberQueries(crudService);
}
如果出于某种原因,您只想模拟某些对象的选定方法,而不是整个对象,您可以使用Mockito来实现。查看this示例。
https://stackoverflow.com/questions/26609499
复制相似问题