为什么$ MockitoMock $实例不被识别为模拟?

我使用Kotlin,Mockito和MockitoJRunner运行以下简化测试:

open class SomeClassToBeMocked @Inject constructor() { fun map(foo: Foo): Bar {...} } @Mock private lateinit var someMock: SomeClassToBeMocked @InjectMocks private lateinit var subject: Subject @Test fun shouldAssertSomething() { val foo = Foo() // from Foo.kt val bar = Bar() // from Bar.java from *another module* whenever(someMock.map(foo)).thenReturn(bar) // breakpoint[1] subject.myMethod(foo) verify(someMock).map(foo) } 

这种模式在代码的其他部分工作,但不是在这个特定的测试中,我得到以下错误信息:

 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. 

当我在breakpoint[1]调试代码时,我可以看到someMock是类SomeClassToBeMocked$MockitoMock$一个实例。 此外,如果我在breakpoint[1]之前立即调用someMock.map(foo) ,它确实运行原始方法,而不是像所有模拟一样返回null

任何想法可能发生在这里?

更新-1 :我已经检查了错误消息中的选项1的所有可能性。 我也试过mock(SomeClassToBeMocked::class.java)并得到相同的错误信息。 这一直发生在一些最简单的类中,只有一个公共方法只把对象A转换成对象B.

更新-2 :如果它有什么区别,我只注意到,在这种情况下, Bar()生活在一个不同的模块比测试。 我已经更新了代码来反映这一点。

更新-3 :如果我创建一个名为SomeClassToBeMocked的接口,并将原始类重命名为SomeClassToBeMockedImpl ,那么所有东西都像魅力一样。 但是,我仍然想知道为什么发生这种情况,以及如何避免为此创建一个接口。

为了用Mockito来模拟一个函数的返回值,你需要用open关键字来标记它:

 open class SomeClassToBeMocked @Inject constructor() { open fun map(foo: Foo): Bar {...} } 

问题是Mockito不能嘲笑final(不能被重写)的函数。 与Java不同,Kotlin需要对可覆盖的成员进行明确的注释。