Mockito Stubbed Spy有时会调用,有时不调用spied对象方法

时间:2018-02-17 07:55:50

标签: android unit-testing kotlin mockito

问题

@Test中,我如何实现这两个目标;

  1. 从正在测试的Kotlin类中调用一个真正的方法
  2. 将内部调用存根于此类测试中的其他方法。
  3. SCENARIO

    我正在使用以下库;

    testCompile "com.nhaarman:mockito-kotlin:1.5.0"
    testCompile "org.mockito:mockito-inline:2.12.0"
    

    我还有一个简单的kotlin类,其形式为

    class MyClass() {
    
        fun parentFunc() {
            funA()
        }
    
        fun funA() {
            //DOES SOMETHING WHICH I ASSUME IS IRRELEVANT FOR ANSWERING THE QUESTION
        }
    }
    

    使用SPY进行测试

    @Test
    fun myTest() {
        val myClassSpy = spy(MyClass())
        Mockito.doNothing().`when`(myClassSpy.funA())
        //Mockito.doNothing().whenever(myClassSpy.funA()) also throws the same error
    
        myClassSpy.parentFunc()
    
        verify(myClassSpy, times(1)).funA()
    }
    

    引发错误,

    org.mockito.exceptions.misusing.UnfinishedStubbingException: 
    Unfinished stubbing detected here:
    -> at com.nhaarman.mockito_kotlin.MockitoKt.doNothing(Mockito.kt:108)
    
    E.g. thenReturn() may be missing.
    Examples of correct stubbing:
        when(mock.isOk()).thenReturn(true);
        when(mock.isOk()).thenThrow(exception);
        doThrow(exception).when(mock).someVoidMethod();
    Hints:
     1. missing thenReturn()
     2. you are trying to stub a final method, which is not supported
     3: you are stubbing the behaviour of another mock inside before 'thenReturn' instruction if completed
    

    另一个测试案例;

    @Test
    fun myTest() {
        val myClassSpy = Mockito.spy(MyClass())
    
        myClassSpy.parentFunc()
    
        verify(myClassSpy, times(1)).funA()
    }
    

    给出以下错误:

    Wanted but not invoked:
    myClass.funA();
    
    However, there was exactly 1 interaction with this mock:
    myClass.parentFunc();
    

    此外,无论何时我尝试使用调试器来调用myClassSpy方法或与之相关的东西,它都会抛出以下错误:

    com.sun.jdi.InternalException : Unexpected JDWP Error: 41
    

    enter image description here

    我试图使用

    Mockito.`when`(myClassSpy.funA()).then { }
    Mockito.`when`(myClassSpy.funA()).thenAnswer { }
    Mockito.`when`(myClassSpy.funA()).thenReturn(Unit)
    

    使用模拟器进行测试

    在这种情况下模拟整个类不起作用,因为它是一个模拟并且不会调用被测试的真实方法:

    @Test
    fun myTest() {
        val myMock: MyClass = mock()
    
        myMock.parentFunc()
    
        verify(myMock, times(1)).funA()
    }
    

    同样的错误:

    Wanted but not invoked:
    myClass.funA();
    
    However, there was exactly 1 interaction with this mock:
    myClass.parentFunc();
    

    如果我进一步调用真实方法,它也会显示相同的wanted but not invoked myClass.funA();错误:

    @Test
    fun myTest() {
        val myMock: MyClass = mock()
    
        Mockito.`when`(myMock.parentFunc()).thenCallRealMethod()
    
        myMock.parentFunc()
    
        verify(myMock, times(1)).funA()
    }
    

    我也试过打开MyClass但是犯了同样的错误。

    因此,我如何从间谍中存储方法,以便当我从这种间谍对象测试方法时,它不会将调用传播到我不想进一步模拟的其他方法。

    任何帮助,建议,想法......为了测试这些类型的方法,我们深表赞赏。

2 个答案:

答案 0 :(得分:0)

对我来说,使用mockito模拟kotlin类有时候会有效,有时它却没有,所以我通常做的是为该类提取一个,这总是有效的。

mocks也用于测试交互,所以你不应该在你正在测试的同一个类上模拟一个方法,而是在传递给构造函数的另一个类(依赖注入),然后你可以在测试时注入一个mock并且正确生产时的依赖性。

class MyTestedClass(val funManager:MyUsedClass) {    
    fun parentFunc() {
        funManager.funA()
    }
}

答案 1 :(得分:0)

解决方案:

  • 当代码库全部位于 Kotlin 中时,解决方案是:
    使用MockK,这是一个用于测试和模拟Kotlin代码的库。
  • 使用 Java和Kotlin 编写代码库时,解决方案是:
    使用MockK来测试和模拟Kotlin,使用Mockito来测试和模拟Java代码。