检查单元测试中是否委托了所有方法 [英] Checking in a unit test whether all methods are delegated

查看:87
本文介绍了检查单元测试中是否委托了所有方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有以下课程

public abstract class Foo{

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

和一个基类,以使其更容易为该类编写装饰器

and a base class to make it easier to write decorators for this class

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 编写装饰器,只在其中覆盖所需的方法。

The class FooWrapper allows you to write a decorator for Foo where you only override the methods you need.

现在,我想为 FooWrapper 编写一个测试,检查默认情况下是否委托了所有方法。当然,我可以写类似

Now I want to write a test for FooWrapper which checks whether all methods are by default delegated. Of course I can write something like

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

但这要求我每次添加方法都添加一个新的测试方法到 Foo 。我希望每次我向 Foo 添加方法时都会失败,而我忘记覆盖并委托 FooWrapper

But this requires me to add a new test method each time a method is added to Foo. I was hoping to have a test which would fail each time I add a method to Foo which I forget to override and delegate in FooWrapper.

我试图使用反射功能,该功能允许我调用每个方法,但是我不知道如何检查该方法是否真正被委托。请参见以下代码片段,了解我的想法:

I was trying to use reflection which allows me to call each method, but I do not know how to check whether the method is actually delegated. See the following snippet for the idea I was toying with:

  @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。

Any ideas whether it is possible to write such a test. Note that the only mocking framework I can use is Mockito.

推荐答案

此代码似乎可以解决问题。如果我在Foo中添加了一个方法,但未将其包含在FooWrapper中,则测试将失败。

This code seems to do the trick. If I add a method to Foo and do not include it in FooWrapper, the test fails.

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

您的代码中缺少的关键行是

The key line here which was missing in your code is

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

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

It might look a little odd, but this works because it invokes things in the same order Mockito expects: first Mockito.verify(delegate) is called (which internally starts the Mockito verification), then the method is invoked. A similar non-reflection invocation would look like Mockito.verify(delegate).foo(). Use this 'why' to help adapt the code to different use cases without breaking how the test does verification.

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

One word of caution, I would add a check at the beginning of each of your loops which iterate over the result of getDeclaredMethods(). This method returns all methods whether they are public, private, protected etc. Attempting to access an inaccessible method throws an exception. You can use Method.isAccessible() to check for this.

这篇关于检查单元测试中是否委托了所有方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆