PowerMockito Spy:使用 CannotStubVoidMethodWithReturnValue 调用真实方法结果

PowerMockito Spy: Calling real method results with CannotStubVoidMethodWithReturnValue

我开始用头撞墙了,但我完全不明白如何在 spied 上调用 real 方法 class。

长话短说,我正在监视我的测试 class 以存根一个私有方法。然后我想调用测试 class 的真正无效方法,但它说

'testedMethod' is a void method and it cannot be stubbed with a return value!

问题是我不需要它被存根。我也试过 doCallRealMethod(),没用。

我正在使用 Kotlin 和 Android Studio 为我的 Android 项目 运行 一些单元测试。我想测试以下 class:

class MyClass {
   fun persistDeviceData(deviceInfo: DeviceInfo, saveCallback: SaveCallback) {
       val deviceObject = getDeviceObject()
          // some setters from deviceInfo here
       deviceObject.saveInBackground(callback)
   }

   // This method is created for unit testing purposes
   private fun getDeviceObject(): MyDeviceObject{
       return MyDeviceObject("someStringArgument")
   }
}

这里正在测试class

import io.kotlintest.KTestJUnitRunner
import io.kotlintest.specs.BehaviorSpec
import junit.framework.Assert
import org.junit.runner.RunWith
import org.mockito.Mockito

import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.modules.junit4.PowerMockRunner
import org.powermock.modules.junit4.PowerMockRunnerDelegate

@RunWith(PowerMockRunner::class)
@PowerMockRunnerDelegate(KTestJUnitRunner::class)
@PrepareForTest(MyClass::class)
class MyClassTest: BehaviorSpec(){

    init {

        val deviceObject = PowerMockito.mock(MyDeviceObject::class.java)
        val myClassTest = PowerMockito.spy(MyClass())
        // Mocking private method call
        PowerMockito.doReturn(deviceObject).`when`(testedRepo,
                PowerMockito.method(
                        MyClass::class.java,
                        "getDeviceObject"))
        val a = SaveCallback { // Callback handling here }
        // Calling REAL method of spy class. Here it fails
        myClassTest.persistDeviceData(DeviceInfo(), a)
        Assert.assertTrue(true)
    }

}

在下一行调用实际方法时测试失败

myClassTest.persistDeviceData(DeviceInfo(), a)

这是错误堆栈跟踪

org.mockito.exceptions.misusing.CannotStubVoidMethodWithReturnValue: 'persistDeviceData' is a void method and it cannot be stubbed with a return value! Voids are usually stubbed with Throwables: doThrow(exception).when(mock).someVoidMethod(); If you need to set the void method to do nothing you can use: doNothing().when(mock).someVoidMethod(); For more information, check out the javadocs for Mockito.doNothing().


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. The method you are trying to stub is overloaded. Make sure you are calling the right overloaded version. 2. Somewhere in your test you are stubbing final methods. Sorry, Mockito does not verify/stub final methods. 3. 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. 4. Mocking methods declared on non-public parent classes is not supported.

at MyClassTest.<init>(MyClass.kt:46)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at java.lang.Class.newInstance(Class.java:442)
at io.kotlintest.KTestJUnitRunner.<init>(KTestJUnitRunner.kt:9)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at org.powermock.reflect.internal.WhiteboxImpl.createInstance(WhiteboxImpl.java:1414)
at org.powermock.reflect.internal.WhiteboxImpl.invokeConstructor(WhiteboxImpl.java:1262)
at org.powermock.reflect.Whitebox.invokeConstructor(Whitebox.java:497)
at org.powermock.modules.junit4.internal.impl.DelegatingPowerMockRunner.call(DelegatingPowerMockRunner.java:101)
at org.powermock.modules.junit4.internal.impl.DelegatingPowerMockRunner.call(DelegatingPowerMockRunner.java:97)
at org.powermock.modules.junit4.internal.impl.DelegatingPowerMockRunner.withContextClassLoader(DelegatingPowerMockRunner.java:132)
at org.powermock.modules.junit4.internal.impl.DelegatingPowerMockRunner.createDelegate(DelegatingPowerMockRunner.java:96)
at org.powermock.modules.junit4.internal.impl.DelegatingPowerMockRunner.<init>(DelegatingPowerMockRunner.java:64)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.createDelegatorFromClassloader(JUnit4TestSuiteChunkerImpl.java:165)
at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.createDelegatorFromClassloader(JUnit4TestSuiteChunkerImpl.java:47)
at org.powermock.tests.utils.impl.AbstractTestSuiteChunkerImpl.createTestDelegators(AbstractTestSuiteChunkerImpl.java:107)
at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.<init>(JUnit4TestSuiteChunkerImpl.java:69)
at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.<init>(AbstractCommonPowerMockRunner.java:36)
at org.powermock.modules.junit4.PowerMockRunner.<init>(PowerMockRunner.java:34)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at org.junit.internal.builders.AnnotatedBuilder.buildRunner(AnnotatedBuilder.java:104)
at org.junit.internal.builders.AnnotatedBuilder.runnerForClass(AnnotatedBuilder.java:86)
at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
at org.junit.internal.builders.AllDefaultPossibilitiesBuilder.runnerForClass(AllDefaultPossibilitiesBuilder.java:26)
at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
at org.junit.internal.requests.ClassRequest.getRunner(ClassRequest.java:33)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:49)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

回答我自己的问题 - 我错误地使用了 PowerMockito 的 .when() 方法。这是有效的代码:

@RunWith(PowerMockRunner::class)
@PrepareForTest(MyClass::class, DeviceObject::class)
class DeviceRepositoryImplTest{

    private val testedRepo = PowerMockito.spy(MyClass())
    private val deviceObject = PowerMockito.mock(DeviceObject())

    @Test
    fun persistDeviceData_ok(){
        // Mocking private method
        PowerMockito.`when`<ParseObject>(testedRepo,
            PowerMockito.method(
                        MyClass::class.java,
                            "getDeviceObject")).withNoArguments().thenReturn(deviceObject)

        // Creating callback for tested method
        val callback = SaveCallback {
            ...
        }

        ...

        testedRepo.persistDeviceData(DeviceInfo(), callback)

    }