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.
The answer to the more general question is yes, you should unit test everything you can. Doing so creates a legacy for later so changes down the road can be done with peace of mind. It ensures that your code works as expected.
Unit testing is a software development process in which the smallest testable parts of an application, called units, are individually and independently scrutinized for proper operation. This testing methodology is done during the development process by the software developers and sometimes QA staff.
So yes, you would test private and protected methods if you felt they needed to be tested for you to answer Yes to the question. Once you've got working code, you need to have a mechanism in place to protect this code from future damage.
In C#, delegates are similar to pointers available in C++. It is basically a reference type variable that contains a reference to another method. Further, its reference cannot be changed during the run time. It is available inside System.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With