检查单元测试是否委托了所有方法

2024-02-08

假设我有以下课程

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);

看起来可能有点奇怪,但是这是有效的,因为它以 Mockito 期望的相同顺序调用事物: first Mockito.verify(delegate)被调用(内部启动 Mockito 验证),然后调用该方法。类似的非反射调用看起来像Mockito.verify(delegate).foo()。使用这个“原因”可以帮助使代码适应不同的用例,而不会破坏测试进行验证的方式。

需要注意的是,我会在每个循环的开头添加一个检查,该检查会迭代 getDeclaredMethods() 的结果。此方法返回所有方法,无论它们是公共的、私有的、受保护的等。尝试访问不可访问的方法会引发异常。您可以使用 Method.isAccessible() 来检查这一点。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

检查单元测试是否委托了所有方法 的相关文章

随机推荐