我有以下测试,我需要验证是否正在调用Person类的所有getter。到目前为止,我已经使用了mockito的verify()来确保调用每个getter。有没有办法通过反思来做到这一点?可能会在Person类中添加一个新的getter,但测试将会错过。
public class GetterTest {
class Person{
private String firstname;
private String lastname;
public String getFirstname() {
return firstname;
}
public String getLastname() {
return lastname;
}
}
@Test
public void testAllGettersCalled() throws IntrospectionException{
Person personMock = mock(Person.class);
personMock.getFirstname();
personMock.getLastname();
for(PropertyDescriptor property : Introspector.getBeanInfo(Person.class).getPropertyDescriptors()) {
verify(personMock, atLeast(1)).getFirstname();
//**How to verify against any getter method and not just getFirstName()???**
}
}
}
答案 0 :(得分:8)
一般来说,不要嘲笑被测试的课程。如果你的测试是针对一个人的,你就不应该在其中看到spy(new Person())
,因为这是一个非常明确的迹象,表明你正在测试模拟框架而不是系统 - 测试
相反,您可能想要创建一个MockingDetails.getInvocations()
,它将使用真实构造函数创建一个真实的Person实现,然后将其数据复制到Mockito生成的代理。您可以使用// This code is untested, but should get the point across. Edits welcome.
// 2016-01-20: Integrated feedback from Georgios Stathis. Thanks Georgios!
@Test
public void callAllGetters() throws Exception {
Person personSpy = spy(new Person());
personSpy.getFirstname();
personSpy.getLastname();
assertAllGettersCalled(personSpy, Person.class);
}
private static void assertAllGettersCalled(Object spy, Class<?> clazz) {
BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
Set<Method> setOfDescriptors = beanInfo.getPropertyDescriptors()
.stream()
.map(PropertyDescriptor::getReadMethod)
.filter(p -> !p.getName().contains("getClass"))
.collect(Collectors.toSet());
MockingDetails details = Mockito.mockingDetails(spy);
Set<Method> setOfTestedMethods = details.getInvocations()
.stream()
.map(InvocationOnMock::getMethod)
.collect(Collectors.toSet());
setOfDescriptors.removeAll(setOfTestedMethods);
// The only remaining descriptors are untested.
assertThat(setOfDescriptors).isEmpty();
}
反射检查是否已调用每个getter。
verify
可能有办法在Mockito生成的间谍上调用invoke
和<tr>
,但这似乎非常脆弱,并且非常依赖于Mockito内部。
顺便说一下,测试bean风格的getter似乎是时间/精力的奇怪用法。通常侧重于测试可能会改变或破坏的实现。
答案 1 :(得分:0)
我可以为您的问题考虑两种解决方案:
以编程方式生成Builder代码,因此您无需运行测试。 Java代码由程序生成,永远不会由用户编辑。改为测试发电机。使用文本模板并从序列化域模型构建定义或直接从Java编译类中构建定义(您需要一个独立于模块的模块)
针对代理库编写测试。问题是常规代理只能实现接口而不是常规类,并且为Javabeans提供接口非常麻烦。如果您选择此路线,我会选择Javassist。我编写了一个可运行的样本并将其on GitHub。测试用例使用代理工厂来实例化bean(而不是使用new
)
public class CountingCallsProxyFactory {
public <T> T proxy(Class<T> classToProxy) {
ProxyFactory factory = new ProxyFactory();
factory.setSuperclass(classToProxy);
Class clazz = factory.createClass();
T instance = (T) clazz.newInstance();
ProxyObject proxy = (ProxyObject) instance;
MethodCallCounter handler = new MethodCallCounter();
proxy.setHandler(handler);
return instance;
}
public void verifyAllGettersCalled(Object bean) {
// Query the counter against the properties in the bean
}
}
计数器保留在班级MethodCallCounter