android - 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)
解决方案
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)
}
推荐阅读
- windows - 如何通过 PowerShell 或 java 在 Windows 资源管理器中获取“状态”列值
- c++ - 在 2D 向量上执行单行算术以找到简化的行形式 C++
- vue.js - 如何定位编辑按钮?
- c# - ASP.NET MVC 中的 Ajax 重定向
- java - 如何在 Room Android 中使用具有多个实体的单个模型?
- docker - Docker 文件更新量
- flutter - Flutter - 健康包未加载 STEP 数据
- c# - 在 List.ForEach 中声明 Func<> 会导致内存泄漏并增加进程内存吗?
- python - 带有两个 pyc 脚本的 Python ValueError
- spring - 我们如何将 Apple Provider 与 Spring Security oauth2 客户端一起使用