首页 > 解决方案 > PowerMockito Spy: Calling real method results with CannotStubVoidMethodWithReturnValue

问题描述

I'm starting to bang my head against the wall, but I completely don't understand how to call real method on spied class.

Long story short, I'm spying on my tested class to stub one private method. Then I want to call real void method of tested class but it says that

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

The problem is that I don't need it to be stubbed. I have tried doCallRealMethod() as well, no use.

I'm using Kotlin and Android Studio to run some unit tests for my Android project. I have following class that I want to test:

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")
   }
}

And here is testing 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)
    }

}

Test fails when calling real method on following line

myClassTest.persistDeviceData(DeviceInfo(), a)

Here's the error stack trace

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$1.call(DelegatingPowerMockRunner.java:101)
at org.powermock.modules.junit4.internal.impl.DelegatingPowerMockRunner$1.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)

标签: androidunit-testingkotlinmockingpowermockito

解决方案


Answering my own question - I incorrectly user PowerMockito's .when() method. Here's the code that works:

@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)

    }

推荐阅读