有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java为什么$MockitoMock$实例没有被标识为mock?

我使用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),它确实会运行原始方法,而不是像所有mock一样返回null

你知道这里会发生什么吗

更新-1:我已检查了错误消息中选项1的所有可能性。我还尝试了mock(SomeClassToBeMocked::class.java),得到了相同的错误消息。对于一些最简单的类来说,这种情况是一致的,只有一个公共方法将对象a转换为对象B

UPDATE-2:如果这有什么不同,我只是注意到,在发生这种情况的情况下,Bar()位于与测试不同的模块中。我已经更新了代码以反映这一点

UPDATE-3:如果我创建了一个名为SomeClassToBeMocked的接口,并将原始类重命名为SomeClassToBeMockedImpl,那么一切工作都很顺利。然而,我仍然想弄清楚为什么会发生这种情况,以及如何避免为此创建接口


共 (1) 个答案

  1. # 1 楼答案

    为了用Mockito模拟函数的返回值,需要用open关键字对其进行标记:

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

    问题是Mockito无法模拟final(不能重写)函数。与Java不同,Kotlin需要为可重写成员提供显式注释