Question

Suppose I have the following class

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

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

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

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

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

Was it helpful?

Solution

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

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.

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top