检查单位测试是否委派所有方法



假设我有以下类

public abstract class Foo{
  public int bar(){
    //implementation
  }
  public abstract int bar2();
}

和一个基础课程,可以更轻松地为此类编写装饰师

public class FooWrapper{
  private final Foo delegate;
  protected FooWrapper( Foo delegate ){
    this.delegate = delegate;
  }
  @Override
  public int bar(){
    return delegate.bar()
  }
  @Override
  public int bar2(){
    return delegate.bar2();
  }
}

FooWrapper允许您为Foo编写一个装饰符,在其中您仅覆盖所需的方法。

现在,我想编写FooWrapper测试,该测试默认情况下所有方法是否均为委托。我当然可以写

之类的东西
@Test
public void barShouldBeDelegated(){
  Foo delegate = Mockito.mock( Foo.class );
  FooWrapper wrapper = new FooWrapper( delegate );
  wrapper.bar();
  Mockito.verify( delegate ).bar();
}

,但这需要我每次将方法添加到Foo时添加一种新的测试方法。我希望每次将方法添加到Foo中,我忘记覆盖并委派在FooWrapper中。

我正在尝试使用反射,这使我可以调用每种方法,但是我不知道如何检查该方法是否实际委派。请参阅以下片段,以了解我在玩耍的想法:

  @Test
  public void testAllMethodsAreDelegated() throws Exception{
    Foo delegate = mock(Foo.class);
    FooWrapper wrapper = new FooWrapper(delegate);
    Class<?> clazz = wrapper.getClass();
    Method[] methods = clazz.getDeclaredMethods();
    for (Method method : methods) {
      Class<?>[] parameterTypes = method.getParameterTypes();
      Object[] arguments = new Object[parameterTypes.length];
      for (int j = 0; j < arguments.length; j++) {
        arguments[j] = Mockito.mock(parameterTypes[j]);
      }
      method.invoke(wrapper, arguments);
      // ?? how to verify whether the delegate is called
      // Mockito.verify( delegate ).??? does not work 
      // as I cannot specify the method by name
      }
    }
  }

是否有可能编写这样的测试。请注意,我唯一可以使用的模拟框架是Mockito。

此代码似乎可以解决问题。如果我在foo中添加了一种方法,并且不将其包括在foowrapper中,则测试失败。

    FooWrapper wrapper = new FooWrapper(delegate);
    Foo delegate = Mockito.mock(Foo.class);
    // For each method in the Foo class...
    for (Method fooMethod : Foo.class.getDeclaredMethods()) {
        boolean methodCalled = false;
        // Find matching method in wrapper class and call it
        for (Method wrapperMethod : FooWrapper.class.getDeclaredMethods()) {
            if (fooMethod.getName().equals(wrapperMethod.getName())) {
                // Get parameters for method
                Class<?>[] parameterTypes = wrapperMethod.getParameterTypes();
                Object[] arguments = new Object[parameterTypes.length];
                for (int j = 0; j < arguments.length; j++) {
                    arguments[j] = Mockito.mock(parameterTypes[j]);
                }
                // Invoke wrapper method
                wrapperMethod.invoke(wrapper, arguments);
                // Ensure method was called on delegate exactly once with the correct arguments
                fooMethod.invoke(Mockito.verify(delegate, Mockito.times(1)), arguments);
                // Set flag to indicate that this foo method is wrapped properly.
                methodCalled = true;
            }
        }
        assertTrue("Foo method '" + fooMethod.getName() + "' has not been wrapped correctly in Foo wrapper", methodCalled);
    }

您代码中缺少的关键行是

fooMethod.invoke(Mockito.verify(delegate, Mockito.times(1)), arguments);

它看起来可能有些奇怪,但是这起作用,因为它以相同的顺序调用事物operito期望:第一个 Mockito.verify(delegate)被称为(内部启动了overito验证),然后调用该方法。类似的非反射调用看起来像Mockito.verify(delegate).foo()。使用此"为什么"来帮助将代码适应不同的用例,而无需打破测试的验证方式。

一个谨慎的单词,我将在您的每个循环的开头添加一张检查,该检查在GetDeclaredMethods()的结果上都迭代。此方法返回所有方法,无论它们是公共,私人,受保护等。您可以使用Method.isaccessible()检查此。

相关内容

  • 没有找到相关文章

最新更新