我有以下测试,我需要验证是否调用了 Person 类的所有 getter。到目前为止,我已经使用了 mockito 的 verify() 来确保调用每个 getter。有没有办法通过反思来做到这一点?可能是将新的 getter 添加到 Person 类中,但测试会错过这一点。
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()???**
}
}
}
一般来说,不要嘲笑被测试的类。如果你的测试是针对一个人的,你不应该在其中看到Mockito.mock(Person.class)
,因为这是一个非常明显的迹象,表明你正在测试模拟框架而不是被测系统。
相反,您可能想要创建一个 spy(new Person())
,它将使用真实的构造函数创建一个真实的 Person 实现,然后将其数据复制到 Mockito 生成的代理。您可以使用MockingDetails.getInvocations()
反射性地检查是否调用了每个 getter。
// 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();
}
可能有一种方法可以调用verify
和invoke
Mockito生成的间谍,但这似乎非常脆弱,并且非常依赖于Mockito的内部结构。
顺便说一句,测试豆式的getter似乎是一种奇怪的时间/精力使用。通常专注于测试可能更改或中断的实现。
我可以为您的问题想到两种解决方案:
-
以编程方式生成生成器代码,因此无需运行测试。Java 代码由程序生成,从不由用户编辑。改为测试生成器。使用文本模板并从序列化域模型或直接从 Java 编译类构建定义(您需要一个依赖于 Bean 的单独模块)
-
针对代理库编写测试。问题是常规代理只能实现接口,而不能实现常规类,并且拥有 Javabeans 接口非常麻烦。如果你选择这条路线,我会选择Javassist。我编写了一个可运行的示例并将其放在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
内