Mockito – 存根抽象父类方法
我看到非常奇怪的行为试图存根一个在抽象父类MyAbstractBaseClass
定义的类MyClass
的方法myMethod(param)
。
当我尝试存根(使用doReturn("...").when(MyClassMock).myMethod(...)
等)此方法失败时,在不同的场景下抛出不同的exception。 在该行上抛出exception。
当我使用doReturn("...").when(MyClassMock).myMethod(CONCRETE PARAM CLASS OBJECT)
,我得到以下exception:
org.mockito.exceptions.misusing.WrongTypeOfReturnValue: String cannot be returned by hasValidExpirationDate() hasValidExpirationDate() should return boolean at ...
hasValidExpirationDate()
不是一个被存根的方法,但它是由抽象基类中MyMethod(param)
的真实实现MyMethod(param)
。
当我使用doReturn("...").when(MyClassMock).myMethod(any(PARAMCLASS.class))
,我得到以下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:
等
但是当我在子类MyClass
定义方法myMethod(param)
,代码不再失败。 我在MyClass
具体实现只是调用super.myMethod(param)
并返回它,因此除了修复unit testing之外它没有任何影响。 所以看起来Mockito只能在类中定义的存根方法被模拟自己,而不是超类。
我正在阅读Mockito文档,我不知道它所说的inheritance方法无法被删除。
myMethod(param)
既不是static
也不是final
。
码:
BaseCard
:
import java.io.Serializable; public class BaseCard implements Serializable { public boolean hasValidExpirationDate() { return true; } }
类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"; } }
DecryptedCard
类:
public class DecryptedCard extends Card { }
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(); } }
失败:
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)
根据这个SO答案 ,当父类是非公开的时,模拟行为不能得到保证,如问题212中所述 。
(感谢Brice在另一个post中给出了很好的答案,感谢Vladimir,JB Nizet和acdcjunior在评论主题中共享调试进度!)
哇! 就是这样!
我得到了奇怪的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"))
虽然一切都是正确的。 我试图从mock类的超类中模拟方法,这个超类与mock类在同一个文件中,所以它不公开,并且出现了那些奇怪的错误。 在将超类移动到单独的文件并将其设置为公共之后,问题就消失了!