学向勤中得,萤窗万卷书。这篇文章主要讲述Android架构组件GithubBrowserSample单元测试理解相关的知识,希望能为你提供帮助。
嗨伙计们我正在尝试使用Google's github browser sample as a base学习Kotlin的单元测试。我的代码确实非常相似,但我不能让他们的一个更基本的测试工作(我真的不明白它)。
我的主要问题是sendResultToUI()
测试究竟是做什么的,
@Test
public void sendResultToUI() {
MutableLiveData<
Resource<
User>
>
foo = new MutableLiveData<
>
();
when(userRepository.loadUser("foo")).thenReturn(foo);
Observer<
Resource<
User>
>
observer = mock(Observer.class);
userViewModel.getUser().observeForever(observer);
userViewModel.setLogin("foo");
verify(observer, never()).onChanged(any(Resource.class));
User fooUser = TestUtil.createUser("foo");
Resource<
User>
fooValue = https://www.songbingjia.com/android/Resource.success(fooUser);
foo.setValue(fooValue);
verify(observer).onChanged(fooValue);
reset(observer);
}
据我所知,它说:
- a)调用
loadUser("foo")
时,只需返回一个名为foo的新实时数据,而不是执行该函数。 - b)观察
userViewModel.getUser()
实时数据 - c)调用
setLogin("foo")
,它触发实时数据并调用loadUser("foo")
- d)验证我们之前创建的
getUser()
的观察者从未被任何资源实例触发过 - e)创建一个成功的foo用户,验证设置它的值会触发
getUser()
观察者
java.lang.IllegalStateException: ArgumentMatchers.any(T::class.java) must not be null
所以我猜
onChanged
是用空值调用的。我真的不知道到底发生了什么 - 在步骤c中调用setLogin()
触发用户switchMap实时数据,然后调用userRepositiory.loadUser()
,所以应该调用观察者getUser()
但我们要求验证相反的(它是从未打过电话)。在所有调用loadUser()之后返回我们在a)中指定的foo。也许如果有人能够至少向我解释测试,我可能会理解我自己的代码!编辑:这是我自己当前的单元测试,类和模型已经改变,但据我所知,实际代码是相同的(我知道这可能更简洁,以后会担心!)
@Test
fun `send result to UI`(){
val foo = MutableLiveData<
Resource<
Member>
>
()
`when`(interactor.callServerLoginRepo(email, password)).thenReturn(foo)
val observer: Observer<
Resource<
Member>
>
= mock()
loginViewModel.member.observeForever(observer)
loginViewModel.setLoginCredentials(email, password)
verify<
Observer<
Resource<
Member>
>
>
(observer, never()).onChanged(any(Resource::class.java) as Resource<
Member>
)
val fooUser = TestUtil.createMember(email)
val fooValue = https://www.songbingjia.com/android/Resource.success(fooUser)foo.setValue(fooValue)
verify<
Observer<
Resource<
Member>
>
>
(observer).onChanged(fooValue)
reset<
Observer<
Resource<
Member>
>
>
(observer)
}
MockitoHelpers.kt
fun <
T>
any(type: Class<
T>
): T = Mockito.any<
T>
(type)
错误也略有不同:
kotlin.TypeCastException: null cannot be cast to non-null type app.core.sdk.data.remote.response.Resource<
app.core.sdk.data.model.db.Member>
at app.core.sdk.ui.login.LoginViewModelTest.send result to UI(LoginViewModelTest.kt:114)
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:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.rules.TestWatcher$1.evaluate(TestWatcher.java:55)
编辑2,最终代码:所以看起来我的主要问题是对不同的
any()
函数的混淆,基本上我需要对any()
进行null安全调用,理想情况下可以指定匹配类。 Mockito-Kotlin库看起来是目前最安全的路径,因为我需要一个any
函数,它将回退到我指定的类,我认为他的版本是这样的:inline fun <
reified T : Any>
any() = Mockito.any(T::class.java) ?: createInstance<
T>
()
我假设观察者用空值触发的原因只是
Mockito.any()
函数所做的事情,而这就是Kotlin抛出异常的地方。val foo = MutableLiveData<
Resource<
Member>
>
()
//When callServerLoginRepo() is called, return foo live data
`when`(interactor.callServerLoginRepo(email, password)).thenReturn(foo)
//Observe member live data
val observer: Observer<
Resource<
Member>
>
= mock()
loginViewModel.member.observeForever(observer)
//Fire setLoginCredentials, and make sure it didn't touch our observed 'member' live data
loginViewModel.setLoginCredentials(email, password)
verify(observer, never()).onChanged(any())
//Create a successful foo user, and set it's value
val fooUser = TestUtil.createMember(email)
val fooValue = https://www.songbingjia.com/android/Resource.success(fooUser)foo.value = fooValue
//Ensure setting this did indeed trigger our live data
verify(observer).onChanged(fooValue)
reset(observer)
答案您正确理解了测试。如果你展示你的代码,那么每个人都会更清楚地帮助你。
【Android架构组件GithubBrowserSample单元测试理解】但是让我猜一下:你正在尝试将java代码转换为kotlin代码。在某些时候,您可能在代码中使用Mockito.any()来模拟行为或内部验证表达式。不可能只使用带有kotlin的any()。有一个关于如何解决这个问题的主题:Is it possible to use Mockito in Kotlin?
另一答案您对测试操作的评估大致正确,除了它并不一定意味着
onChanged
被调用null。为了详细说明我的经验,Kotlin中的Mockito有一组扩展,可以提供与Kotlin语言更好的兼容性,以及改进测试语法。
https://github.com/nhaarman/mockito-kotlin
我们使用这些,我推荐它们。但是我们发现如果我们不小心导入,我们导入:
import org.mockito.ArgumentMatchers.any
代替:
import com.nhaarman.mockito_kotlin.any
因此,我们使用一组混合的Java类和Kotlin扩展,然后我们看到你注意到的
ArgumentMatchers.any(T::class.java) must not be null
错误。鉴于您使用的是
MockitoKotlinHelpers
,您的情况可能非常相似。推荐阅读
- 如何更改android地图中的圆圈颜色
- Android(Mockito(2.0.2-beta) - 无法模拟/间谍最后的课程)
- Android Mockito如何模拟资源
- 集成CLion和Android NDK
- Android - 劫持点击[关闭]
- Android ListView performitemclick
- 如何在QML ApplicationWindow中获取activeFocusControl的className
- QML中的SVG未在Android上显示
- 如何阅读web.config文件中的appSettings部分()