Search code examples
mockitomulecglib

Can a mockito mock itself be wrapped at runtime?


If I wrap a mock created by Mockito at runtime and then call a method on the wrapper, the wrapped mock is not called. Please, see below:

This is the test I run:

import static org.mockito.Mockito.verify;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.NoOp;
import org.junit.Test;
import org.mockito.Mockito;

public class MyTest {

    @Test
    public void mockIsCalled() {
        final Bar bar = Mockito.mock(Bar.class);
        final Bar wrapper = wrap(bar);
        wrapper.foo();
        verify(bar).foo();
    }

    @SuppressWarnings("unchecked")
    private <T> T wrap(final T objToWrap) {
        return (T) Enhancer.create(objToWrap.getClass(), NoOp.INSTANCE);
    }

}

where Bar is:

public interface Bar {
    String foo();
}

The test fails and the output I get is:

java.lang.NoSuchMethodError: java.lang.Object.foo()Ljava/lang/String;
    at Bar$$EnhancerByMockitoWithCGLIB$$d2b59df8.foo(<generated>)
    at MyTest.mockIsCalled(MyTest.java:18)
...

If I turn Bar into a class as in:

public class Bar {

    public String foo() {
        System.out.println("foo");
        return null;
    }

}

the test continues to fail, foo is printed on the console, and I get the output:

Wanted but not invoked:
bar.foo();
-> at MyTest.mockIsCalled(MyTest.java:20)
Actually, there were zero interactions with this mock.

    at MyTest.mockIsCalled(MyTest.java:20)
...

I am confused.

The real problem that I am trying to solve is to wrap dynamic proxies (injected by Mule via component binding) in order to memoize the method calls on the wrapped dynamic proxies. I want to make it generic enough so that it is sufficient to wrap the dynamic proxy object without having to extend any interface.

Thanks


Solution

  • The problem you're seeing in the case of the Bar class, you would also be seeing in the interface version, if not for the cglib wackiness. You're not wrapping the mock, you're creating a new object. So, the original mock is never exercised.

    For using the class version, have you tried the version of create() that accepts interfaces as params?

    I'm not sure I fully grok your usage scenario, but for something Mockito-specific, you could try taking the proxy created by cglib and then using spy() on that instead of mocking a fresh object.

    I don't know much about cglib, frankly, but perhaps you could implement your own Callback, which contains and delegates to the original object. You could supply that Callback to Enhancer.create() instead of the NoOp one.