Mockito 说实际调用在使用 MutableLiveData 值时有不同的参数
Mockito Says Actual invocation has different arguments when using MutableLiveData Value
请阅读整个描述我已尽力解释代码的每个角落。
Mockito 说实际调用有不同的参数
我正在测试一个更新密码函数,它有这样的代码,
fun update() {
if (uiModel.validateData().isEmpty()) {
changePassword(
uiModel.oldpassword.value ?: "",
uiModel.newpassword.value ?: "",
uiModel.confirmpassword.value ?: ""
)
} else {
showToast(uiModel.validateData()[0])
}
}
uiModel.validateData().isEmpty()
此行验证用户输入是否正确,然后在块内部继续。
uiModel.oldpassword.value
uiModel 是我的支持 class,它有一个 Mutablelive 数据,它通过数据绑定连接到 EditTextView,后者获取和设置数据到视图中。我已经嘲笑了 uiModel class 这是 dagger 在 viewModel 构造函数中提供的简单 class 这里是 uiModel class 代码。
class ChangePasswordUiModel @Inject constructor() {
var oldpassword = MutableLiveData<String>().default("")
var newpassword= MutableLiveData<String>().default("")
var confirmpassword= MutableLiveData<String>().default("")
}
这里是 viewModel 注入设置
@ChangePasswordScope
class ChangePasswordViewModel @Inject constructor(
private val useCase: ChangePasswordUseCase,
val uiModel: ChangePasswordUiModel
) : BaseFragmentViewModel() {
}
如果所有验证都已设置,则会调用此函数。
changePassword(uiModel.oldpassword.value ?: "",
uiModel.newpassword.value ?: "",
uiModel.confirmpassword.value ?: ""
)
实际上是这样的。
var testValue = ""
private fun changePassword(oldpass: String, newpass: String, confirmPass: String) {
viewModelScope.launch {
useCase.changePassword(
oldpass,
newpass,
confirmPass
).let { result ->
when (result) {
is Result.Success -> {
testValue = "Success"
}
is Result.Exception -> showException(result.exception)
is Result.Error -> showError(parseError(result.errorBody))
else -> {
}
}
}
}
}
现在 useCase.changePassword()
这个函数对我来说真的很神奇,它实际上发起了网络请求,return 我是一个自定义密封的 class,它具有三个值 Success(Any()),Error (), 例外。
用例看起来像这样。
interface ChangePasswordUseCase {
suspend fun changePassword(oldpassword: String, newpassword: String, confirmpassword: String): Result
}
现在是测试的问题
我想在 update() 函数调用后检查是否调用了 changepassword
我的测试代码是这样的,
// these are the values which set up in @before
val useCase = mock<ChangePasswordUseCase>()
val uiModel = mock<ChangePasswordUiModel>()
val SUT: ChangePasswordViewModel by lazy { ChangePasswordViewModel(useCase, uiModel) }
@Test
fun `update pass validate pass and change pass`() {
val emptyLiveData = MutableLiveData("abc")
whenever(uiModel.validateData()).thenReturn(mutableListOf())
whenever(uiModel.oldpassword).thenReturn(emptyLiveData)
whenever(uiModel.confirmpassword).thenReturn(emptyLiveData)
whenever(uiModel.newpassword).thenReturn(emptyLiveData)
runBlockingTest {
whenever(
useCase.changePassword(
(emptyLiveData.value!!),
(emptyLiveData.value!!),
(emptyLiveData.value!!)
)
).thenReturn(
Result.Success(
ChangePasswordResponse()
)
)
SUT.update()
verify(useCase).changePassword(
(emptyLiveData.value!!),
(emptyLiveData.value!!),
(emptyLiveData.value!!)
)
assertThat(SUT.testValue).isEqualTo("Success")
}
}
whenever
是我写在Mockito.when
上的扩展函数
最后这个问题花了我一天时间但没有解决...我知道这个问题是价值观参考问题,但我不知道如何解决这个问题
错误
Argument(s) are different! Wanted:
changePasswordUseCase.changePassword(
"abc",
"abc",
"abc",
Continuation at com.trainerhub.trainer.view.home.view.change_password.ChangePasswordViewModelTest$update pass validate pass and change pass.invokeSuspend(ChangePasswordViewModelTest.kt:105)
);
-> at com.trainerhub.trainer.view.home.view.change_password.ChangePasswordViewModelTest$update pass validate pass and change pass.invokeSuspend(ChangePasswordViewModelTest.kt:102)
Actual invocation has different arguments:
changePasswordUseCase.changePassword(
"abc",
"abc",
"abc",
Continuation at com.trainerhub.trainer.view.home.view.change_password.ChangePasswordViewModel$changePassword.invokeSuspend(ChangePasswordViewModel.kt:61)
);
-> at com.trainerhub.trainer.view.home.view.change_password.ChangePasswordViewModel$changePassword.invokeSuspend(ChangePasswordViewModel.kt:58)
Comparison Failure:
<Click to see difference>
Argument(s) are different! Wanted:
changePasswordUseCase.changePassword(
"abc",
"abc",
"abc",
Continuation at com.trainerhub.trainer.view.home.view.change_password.ChangePasswordViewModelTest$update pass validate pass and change pass.invokeSuspend(ChangePasswordViewModelTest.kt:105)
);
-> at com.trainerhub.trainer.view.home.view.change_password.ChangePasswordViewModelTest$update pass validate pass and change pass.invokeSuspend(ChangePasswordViewModelTest.kt:102)
Actual invocation has different arguments:
changePasswordUseCase.changePassword(
"abc",
"abc",
"abc",
Continuation at com.trainerhub.trainer.view.home.view.change_password.ChangePasswordViewModel$changePassword.invokeSuspend(ChangePasswordViewModel.kt:61)
);
-> at com.trainerhub.trainer.view.home.view.change_password.ChangePasswordViewModel$changePassword.invokeSuspend(ChangePasswordViewModel.kt:58)
at com.trainerhub.trainer.view.home.view.change_password.ChangePasswordViewModelTest$update pass validate pass and change pass.invokeSuspend(ChangePasswordViewModelTest.kt:102)
at com.trainerhub.trainer.view.home.view.change_password.ChangePasswordViewModelTest$update pass validate pass and change pass.invoke(ChangePasswordViewModelTest.kt)
at kotlinx.coroutines.test.TestBuildersKt$runBlockingTest$deferred.invokeSuspend(TestBuilders.kt:50)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.test.TestCoroutineDispatcher.dispatch(TestCoroutineDispatcher.kt:50)
at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuation.kt:305)
at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:30)
at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable$default(Cancellable.kt:27)
at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:109)
at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:158)
at kotlinx.coroutines.BuildersKt__Builders_commonKt.async(Builders.common.kt:91)
at kotlinx.coroutines.BuildersKt.async(Unknown Source)
at kotlinx.coroutines.BuildersKt__Builders_commonKt.async$default(Builders.common.kt:84)
at kotlinx.coroutines.BuildersKt.async$default(Unknown Source)
at kotlinx.coroutines.test.TestBuildersKt.runBlockingTest(TestBuilders.kt:49)
at kotlinx.coroutines.test.TestBuildersKt.runBlockingTest$default(TestBuilders.kt:45)
at com.trainerhub.trainer.view.home.view.change_password.ChangePasswordViewModelTest.update pass validate pass and change pass(ChangePasswordViewModelTest.kt:88)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.rules.TestWatcher.evaluate(TestWatcher.java:61)
at org.junit.rules.TestWatcher.evaluate(TestWatcher.java:61)
at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:306)
at org.junit.runners.BlockJUnit4ClassRunner.evaluate(BlockJUnit4ClassRunner.java:100)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at org.junit.runners.ParentRunner.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access0(ParentRunner.java:66)
at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.CommandLineWrapper.main(CommandLineWrapper.java:64)
Process finished with exit code -1
你的代码看起来很完美。
使用协程和挂起函数时,您将需要协程测试和 mockito-kotlin 依赖项。
确保在 build.gralde.
中添加了两个依赖项
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.4.2'
testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"
请阅读整个描述我已尽力解释代码的每个角落。
Mockito 说实际调用有不同的参数 我正在测试一个更新密码函数,它有这样的代码,
fun update() {
if (uiModel.validateData().isEmpty()) {
changePassword(
uiModel.oldpassword.value ?: "",
uiModel.newpassword.value ?: "",
uiModel.confirmpassword.value ?: ""
)
} else {
showToast(uiModel.validateData()[0])
}
}
uiModel.validateData().isEmpty()
此行验证用户输入是否正确,然后在块内部继续。
uiModel.oldpassword.value
uiModel 是我的支持 class,它有一个 Mutablelive 数据,它通过数据绑定连接到 EditTextView,后者获取和设置数据到视图中。我已经嘲笑了 uiModel class 这是 dagger 在 viewModel 构造函数中提供的简单 class 这里是 uiModel class 代码。
class ChangePasswordUiModel @Inject constructor() {
var oldpassword = MutableLiveData<String>().default("")
var newpassword= MutableLiveData<String>().default("")
var confirmpassword= MutableLiveData<String>().default("")
}
这里是 viewModel 注入设置
@ChangePasswordScope
class ChangePasswordViewModel @Inject constructor(
private val useCase: ChangePasswordUseCase,
val uiModel: ChangePasswordUiModel
) : BaseFragmentViewModel() {
}
如果所有验证都已设置,则会调用此函数。
changePassword(uiModel.oldpassword.value ?: "",
uiModel.newpassword.value ?: "",
uiModel.confirmpassword.value ?: ""
)
实际上是这样的。
var testValue = ""
private fun changePassword(oldpass: String, newpass: String, confirmPass: String) {
viewModelScope.launch {
useCase.changePassword(
oldpass,
newpass,
confirmPass
).let { result ->
when (result) {
is Result.Success -> {
testValue = "Success"
}
is Result.Exception -> showException(result.exception)
is Result.Error -> showError(parseError(result.errorBody))
else -> {
}
}
}
}
}
现在 useCase.changePassword()
这个函数对我来说真的很神奇,它实际上发起了网络请求,return 我是一个自定义密封的 class,它具有三个值 Success(Any()),Error (), 例外。
用例看起来像这样。
interface ChangePasswordUseCase {
suspend fun changePassword(oldpassword: String, newpassword: String, confirmpassword: String): Result
}
现在是测试的问题
我想在 update() 函数调用后检查是否调用了 changepassword 我的测试代码是这样的,
// these are the values which set up in @before
val useCase = mock<ChangePasswordUseCase>()
val uiModel = mock<ChangePasswordUiModel>()
val SUT: ChangePasswordViewModel by lazy { ChangePasswordViewModel(useCase, uiModel) }
@Test
fun `update pass validate pass and change pass`() {
val emptyLiveData = MutableLiveData("abc")
whenever(uiModel.validateData()).thenReturn(mutableListOf())
whenever(uiModel.oldpassword).thenReturn(emptyLiveData)
whenever(uiModel.confirmpassword).thenReturn(emptyLiveData)
whenever(uiModel.newpassword).thenReturn(emptyLiveData)
runBlockingTest {
whenever(
useCase.changePassword(
(emptyLiveData.value!!),
(emptyLiveData.value!!),
(emptyLiveData.value!!)
)
).thenReturn(
Result.Success(
ChangePasswordResponse()
)
)
SUT.update()
verify(useCase).changePassword(
(emptyLiveData.value!!),
(emptyLiveData.value!!),
(emptyLiveData.value!!)
)
assertThat(SUT.testValue).isEqualTo("Success")
}
}
whenever
是我写在Mockito.when
最后这个问题花了我一天时间但没有解决...我知道这个问题是价值观参考问题,但我不知道如何解决这个问题
错误
Argument(s) are different! Wanted:
changePasswordUseCase.changePassword(
"abc",
"abc",
"abc",
Continuation at com.trainerhub.trainer.view.home.view.change_password.ChangePasswordViewModelTest$update pass validate pass and change pass.invokeSuspend(ChangePasswordViewModelTest.kt:105)
);
-> at com.trainerhub.trainer.view.home.view.change_password.ChangePasswordViewModelTest$update pass validate pass and change pass.invokeSuspend(ChangePasswordViewModelTest.kt:102)
Actual invocation has different arguments:
changePasswordUseCase.changePassword(
"abc",
"abc",
"abc",
Continuation at com.trainerhub.trainer.view.home.view.change_password.ChangePasswordViewModel$changePassword.invokeSuspend(ChangePasswordViewModel.kt:61)
);
-> at com.trainerhub.trainer.view.home.view.change_password.ChangePasswordViewModel$changePassword.invokeSuspend(ChangePasswordViewModel.kt:58)
Comparison Failure:
<Click to see difference>
Argument(s) are different! Wanted:
changePasswordUseCase.changePassword(
"abc",
"abc",
"abc",
Continuation at com.trainerhub.trainer.view.home.view.change_password.ChangePasswordViewModelTest$update pass validate pass and change pass.invokeSuspend(ChangePasswordViewModelTest.kt:105)
);
-> at com.trainerhub.trainer.view.home.view.change_password.ChangePasswordViewModelTest$update pass validate pass and change pass.invokeSuspend(ChangePasswordViewModelTest.kt:102)
Actual invocation has different arguments:
changePasswordUseCase.changePassword(
"abc",
"abc",
"abc",
Continuation at com.trainerhub.trainer.view.home.view.change_password.ChangePasswordViewModel$changePassword.invokeSuspend(ChangePasswordViewModel.kt:61)
);
-> at com.trainerhub.trainer.view.home.view.change_password.ChangePasswordViewModel$changePassword.invokeSuspend(ChangePasswordViewModel.kt:58)
at com.trainerhub.trainer.view.home.view.change_password.ChangePasswordViewModelTest$update pass validate pass and change pass.invokeSuspend(ChangePasswordViewModelTest.kt:102)
at com.trainerhub.trainer.view.home.view.change_password.ChangePasswordViewModelTest$update pass validate pass and change pass.invoke(ChangePasswordViewModelTest.kt)
at kotlinx.coroutines.test.TestBuildersKt$runBlockingTest$deferred.invokeSuspend(TestBuilders.kt:50)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.test.TestCoroutineDispatcher.dispatch(TestCoroutineDispatcher.kt:50)
at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuation.kt:305)
at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:30)
at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable$default(Cancellable.kt:27)
at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:109)
at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:158)
at kotlinx.coroutines.BuildersKt__Builders_commonKt.async(Builders.common.kt:91)
at kotlinx.coroutines.BuildersKt.async(Unknown Source)
at kotlinx.coroutines.BuildersKt__Builders_commonKt.async$default(Builders.common.kt:84)
at kotlinx.coroutines.BuildersKt.async$default(Unknown Source)
at kotlinx.coroutines.test.TestBuildersKt.runBlockingTest(TestBuilders.kt:49)
at kotlinx.coroutines.test.TestBuildersKt.runBlockingTest$default(TestBuilders.kt:45)
at com.trainerhub.trainer.view.home.view.change_password.ChangePasswordViewModelTest.update pass validate pass and change pass(ChangePasswordViewModelTest.kt:88)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.rules.TestWatcher.evaluate(TestWatcher.java:61)
at org.junit.rules.TestWatcher.evaluate(TestWatcher.java:61)
at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:306)
at org.junit.runners.BlockJUnit4ClassRunner.evaluate(BlockJUnit4ClassRunner.java:100)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at org.junit.runners.ParentRunner.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access0(ParentRunner.java:66)
at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.CommandLineWrapper.main(CommandLineWrapper.java:64)
Process finished with exit code -1
你的代码看起来很完美。
使用协程和挂起函数时,您将需要协程测试和 mockito-kotlin 依赖项。
确保在 build.gralde.
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.4.2'
testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"