Reputation: 291
I am seeing very strange behavior trying to stub a method myMethod(param)
of class MyClass
that is defined in an abstract parent class MyAbstractBaseClass
.
When I try to stub (using doReturn("...").when(MyClassMock).myMethod(...)
etc.) this method fails, different exceptions are thrown under different scenarios. The exceptions are thrown right on that line.
When I use doReturn("...").when(MyClassMock).myMethod(CONCRETE PARAM CLASS OBJECT)
, I get the following exception:
org.mockito.exceptions.misusing.WrongTypeOfReturnValue:
String cannot be returned by hasValidExpirationDate()
hasValidExpirationDate() should return boolean
at ...
hasValidExpirationDate()
is not a method being stubbed, but it is called by the real implementation of MyMethod(param)
in the abstract base class.
When I use doReturn("...").when(MyClassMock).myMethod(any(PARAMCLASS.class))
, I get the following exception:
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:
, etc.
But when I define the method myMethod(param)
in a subclass MyClass
the code no longer fails. My concrete implementation in MyClass
just calls super.myMethod(param)
and returns it, so it has no effect other than fixing the unit test. So it looks like Mockito can only stub methods defined in the class being mocked itself, not in the super classes.
I am reading Mockito documentation and I don't see where it says that inherited methods can't be stubbed.
myMethod(param)
is neither static
nor final
.
Class BaseCard
:
import java.io.Serializable;
public class BaseCard implements Serializable {
public boolean hasValidExpirationDate() {
return true;
}
}
Class Card
:
abstract class Card extends BaseCard {
public Card () { }
public String getUnexpiredStringForNetwork(){
//If the date is invalid return empty string, except for Discover.
if( ! hasValidExpirationDate()){
return "hi";
}
return "hello";
}
}
Class DecryptedCard
:
public class DecryptedCard extends Card {
}
Class MyTest
:
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import org.junit.Test;
public class MyTest {
@Test
public void test() {
DecryptedCard decryptedCardMock = mock(DecryptedCard.class);
doReturn("ABC").when(decryptedCardMock).getUnexpiredStringForNetwork();
}
}
Failure:
org.mockito.exceptions.misusing.WrongTypeOfReturnValue:
String cannot be returned by hasValidExpirationDate()
hasValidExpirationDate() should return boolean
***
If you're unsure why you're getting above error read on.
Due to the nature of the syntax above problem might occur because:
1. This exception *might* occur in wrongly written multi-threaded tests.
Please refer to Mockito FAQ on limitations of concurrency testing.
2. A spy is stubbed using when(spy.foo()).then() syntax. It is safer to stub spies -
- with doReturn|Throw() family of methods. More in javadocs for Mockito.spy() method.
at Card.getUnexpiredStringForNetwork(Card.java:10)
at DecryptedCard.getUnexpiredStringForNetwork(DecryptedCard.java:1)
at MyTest.test(MyTest.java:13)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Upvotes: 4
Views: 3747
Reputation: 124
Wow! That was it!
I was getting strange exception:
org.mockito.exceptions.misusing.InvalidUseOfMatchersException:
Misplaced argument matcher detected here:
-> at com.medziku.motoresponder.logic.ExposedResponder.createSettings(ResponderTest.java:163)
-> at com.medziku.motoresponder.logic.ExposedResponder.createSettings(ResponderTest.java:163)
You cannot use argument matchers outside of verification or stubbing.
Examples of correct usage of argument matchers:
when(mock.get(anyInt())).thenReturn(null);
doThrow(new RuntimeException()).when(mock).someVoidMethod(anyObject());
verify(mock).someMethod(contains("foo"))
While everything was correct. I tried to mock method from superclass of mock class, and this superclass was inside same file as mock class, so it was not public and those strange errors arise. after moving superclass to separate file, and setting it public, problem is gone!
Upvotes: 0
Reputation: 95644
According to this SO answer, mock behavior isn't guaranteed when parent class is non-public, as documented in issue 212.
(Thanks Brice for the good answer in the other thread, and thanks Vladimir, JB Nizet, and acdcjunior for sharing debugging progress in the comments thread!)
Upvotes: 1