【发布时间】:2021-02-10 11:50:19
【问题描述】:
请阅读完整的描述我已尽力解释代码的每个角落。
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 是我的支持类,它有一个 Mutablelive 数据,它通过数据绑定连接到 EditTextView,它获取数据并将数据设置到视图中。我已经模拟了 uiModel 类,它是 dagger 在 viewModel 构造函数中提供的一个简单类,这里是 uiModel 类代码。
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() 这个函数实际上为我做了魔法,它实际上启动了网络请求并返回一个自定义的密封类,它具有三个值 Success(Any())、Error()、Exception。
用例看起来像这样。
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$1.invokeSuspend(ChangePasswordViewModelTest.kt:105)
);
-> at com.trainerhub.trainer.view.home.view.change_password.ChangePasswordViewModelTest$update pass validate pass and change pass$1.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$1.invokeSuspend(ChangePasswordViewModel.kt:61)
);
-> at com.trainerhub.trainer.view.home.view.change_password.ChangePasswordViewModel$changePassword$1.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$1.invokeSuspend(ChangePasswordViewModelTest.kt:105)
);
-> at com.trainerhub.trainer.view.home.view.change_password.ChangePasswordViewModelTest$update pass validate pass and change pass$1.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$1.invokeSuspend(ChangePasswordViewModel.kt:61)
);
-> at com.trainerhub.trainer.view.home.view.change_password.ChangePasswordViewModel$changePassword$1.invokeSuspend(ChangePasswordViewModel.kt:58)
at com.trainerhub.trainer.view.home.view.change_password.ChangePasswordViewModelTest$update pass validate pass and change pass$1.invokeSuspend(ChangePasswordViewModelTest.kt:102)
at com.trainerhub.trainer.view.home.view.change_password.ChangePasswordViewModelTest$update pass validate pass and change pass$1.invoke(ChangePasswordViewModelTest.kt)
at kotlinx.coroutines.test.TestBuildersKt$runBlockingTest$deferred$1.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$1.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$1.evaluate(TestWatcher.java:61)
at org.junit.rules.TestWatcher$1.evaluate(TestWatcher.java:61)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.BlockJUnit4ClassRunner$1.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$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.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
【问题讨论】:
标签: android unit-testing kotlin mockito android-livedata