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
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.
Related
I am working on an API for work, we use a shared library for multiple projects for the purposing of our logging framework. The class used uses all static methods for its calls.
I am trying to Unit test an API call, I can not have it call anything on the Logging class, else it will fail.
I have tried using Powermock, but it fails on
PowerMockito.mockStatic(LoggingFramework.class);
Mockito.when(LoggingFramework.startACall(anyString())).thenReturn("someTimestamp");
returning a
ClassCastException: org.apache.logging.slf4j.SLF4JLoggerContext cannot be cast to org.apache.logging.log4j.core.LoggerContext
the line in LoggingFramework that throws it, is inside a static initializer block outside of any methods in the class.
In order to suppress static initialization you should use #SuppressStaticInitializationFor. So your code will look like this:
#RunWith(PowerMockRunner.class)
#SuppressStaticInitializationFor("so.LoggingFramework") //here goes fully-qualified name of a class
public class LoggingFrameworkTest {
#Test
public void test() {
//given:
PowerMockito.mockStatic(LoggingFramework.class);
Mockito.when(LoggingFramework.foo(anyString())).thenReturn("stub");
//when:
String foo = LoggingFramework.foo("ignored");
//then:
PowerMockito.verifyStatic(LoggingFramework.class, Mockito.times(1));
LoggingFramework.foo(anyString()); //two-step verification of a static method
assertThat(foo, equalTo("stub"));
}
}
Verification of a static method is performed in two steps. It is explained here
I've an interface like this:
public interface ICustomer extends IEnd<Customer> {
String getId();
ICustomer id(String id);
ICustomer email(String email);
ICustomer description(String description);
}
I need to mock any methods which returns an ICustomer regardless of parameters.
When these methods are called, the self called ICustomer have to be returned.
Any ideas?
To do this you need a custom Answer class:
public class CustomerAnswer implements Answer {
#Override
public Object answer(InvocationOnMock invocation) throws Throwable {
Class retType = invocation.getMethod().getReturnType();
if (ICustomer.class.isInstance(retType)) {
return invocation.getMock();
}
// provide default logic here -- override with "when()" calls.
return null;
}
}
Then create your mock, specifying the default behavior:
Foo mockCustomer = mock(ICustomer.class, new CustomerAnswer());
Add, when() statements for other methods that need to be stubbed.
But as I commented in the OP, be sure you actually want to mock this class before you go thru all the trouble. Only mock when it will make the test code simpler. If you have some simple implementation of the interface that is just a POJO with fluent API (no side-effects, no complicated dependencies or injections), there is probably no need to mock it. Instead use a real instance, because the real instance already returns the original object.
If you need to verify() on the ICustomer object, then use a #Spy of a real instance of a ICustomer.
I have something like the following:
public class Foo {
public static String getValue(String key) {
return key + "_" + System.currentTimeMillis();
}
}
Now I need to have a test case for it and here is how I attempted:
#Test
public void testFoo() {
PowerMockito.mockStatic(Foo.class);
PowerMockito.when(Foo.getValue("123")).thenReturn("abcd");
PowerMockito.verifyStatic();
}
When I run the test case, I get this:
org.mockito.exceptions.misusing.MissingMethodInvocationException:
when() requires an argument which has to be 'a method call on a mock'.
For example:
when(mock.getArticles()).thenReturn(articles);
Also, this error might show up because:
1. you stub either of: final/private/equals()/hashCode() methods.
Those methods *cannot* be stubbed/verified.
Mocking methods declared on non-public parent classes is not supported.
2. inside when() you don't call method on mock but on some other object.
at org.powermock.api.mockito.PowerMockito.when(PowerMockito.java:495)
Am I doing anything wrong?
Please advise.
Can you confirm that you've added a #PrepareForTest(Foo.class) annotation onto your test class as listed on the PowerMock site, and that you're using the PowerMockRunner JUnit4 runner?
Static methods are invoked with static dispatch, not dynamic dispatch using Java's normal polymorphism and method overrides. To replace the implementation, PowerMock needs to load a replacement class before the test starts running, and the runner and annotation make that happen.
Suppose I have the following interface:
public interface ISomething {
default int doStuff() {
return 2 * getValue();
}
int getValue();
}
When I now mock this interface like this:
#Mock
private ISomething _something;
#Before
public void setup() {
doCallRealMethod().when(_something).doStuff();
}
and try to test the doStuff() method like the following:
#Test
public void testDoStuff() {
when(_something.getValue()).thenReturn(42);
assertThat("doStuff() returns 84", _something.doStuff(), is(84));
}
I expect the test to succeed, but I get:
org.mockito.exceptions.base.MockitoException:
Cannot call real method on java interface. Interface does not have any implementation!
Calling real methods is only possible when mocking concrete classes.
I tried subclassing ISomething with an abstract class like this:
public abstract class Something implements ISomething {
}
and mock this class like above. With this approach, I get the same.
Does Mockito not support calling default implementations?
That's correct. The current version of Mockito doesn't support this. You could raise a feature request here. Do note that it seems to be related to issue 456 which was fixed in release 1.10.0, so please make sure you test this in the latest version first.
I was working on a project using Mockito 1.9.5 and ran into the same issue that you found. We couldn't upgrade Mockito because of the way our build server worked. The problem we ran into was when we were writing unit tests for the concrete subclasses, as we couldn't stub out or include the default methods from the interface in our mock objects (so slightly different from your example).
Here is an example subclass using your model:
public class ConcreteSomething implements ISomething {
#Override
int getValue()
{
return 42;
}
}
Then in the unit test class, we explicitly made a private inner class. This class overrode all the default methods of the concrete class under test (i.e. ConcreteSomething) with the interface's default implementation. So in this example, something like:
private class ConcreteSomethingDefaultImpl extends ConcreteSomething {
#Override
int doStuff() {
return super.doStuff();
}
}
For us, a mock made using mock(ConcreteSomething.class) couldn't have it's default methods called using doCallRealMethod(), but mock(ConcreteSomethingDefaultImpl.class) could, and more importantly, it was the default implementation code in the interface that was being used.
I hope that helps anyone else who is constrained to use a particular version of Mockito.
I got error while running JUnit test with Mockito's matcher
org.mockito.exceptions.misusing.InvalidUseOfMatchersException:
Invalid use of argument matchers!
0 matchers expected, 1 recorded.
This exception may occur if matchers are combined with raw values:
//incorrect:
someMethod(anyObject(), "raw String");
When using matchers, all arguments have to be provided by matchers.
For example:
//correct:
someMethod(anyObject(), eq("String by matcher"));
Here is source code.
class A
{
public String getField()
{
return "hi";
}
}
#Test
public void testA()
{
final A a = mock(A.class);
when(a.getField()).thenReturn(Matchers.any(String.class));
a.getField();
}
What is problem here? Please open my eyes!
You are using an argument matcher: Matchers.any(String.class). An argument matcher is not intended to be used as a return a value of a stubbed method.
An argument matcher should be used when you need to customize the way a method is stubbed :
when(a.sayHello(Matchers.any(String.class))).thenReturn("Hello");
In your example, you must return an instance of String and not a matcher :
when(a.getField()).thenReturn("theFieldValue");
You have made the classic error of mocking the class that you're trying to test. The whole point of mocking is that you remove other classes from consideration by your test. So if you're testing a class A that uses an object of class B in some way, you might make a mock of class B. But you wouldn't mock the class that you're actually testing, because then you are no longer testing the class, but testing the mocking framework.
In order to test the method you've supplied, it doesn't make sense to use Mockito at all; because there's no additional class that you want to remove from consideration by your test. The only class is A - the one you're testing.
Your test should just be the following
public class TestA {
private A toTest = new A();
#Test
public void getFieldReturnsHi() {
assertEquals("Hi", toTest.getField());
}
}