验证是否已调用所有getter方法

时间:2016-01-13 17:44:42

标签: java unit-testing reflection junit mockito

我有以下测试,我需要验证是否正在调用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()???**
        }
    }
}

2 个答案:

答案 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)

我可以为您的问题考虑两种解决方案:

  1. 以编程方式生成Builder代码,因此您无需运行测试。 Java代码由程序生成,永远不会由用户编辑。改为测试发电机。使用文本模板并从序列化域模型构建定义或直接从Java编译类中构建定义(您需要一个独立于模块的模块)

  2. 针对代理库编写测试。问题是常规代理只能实现接口而不是常规类,并且为Javabeans提供接口非常麻烦。如果您选择此路线,我会选择Javassist。我编写了一个可运行的样本并将其on GitHub。测试用例使用代理工厂来实例化bean(而不是使用new

  3. 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