Android体系结构组件Githubbrowsersame单元测试理解



嗨,我正在尝试使用Google的GitHub浏览器样本作为基础来与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 = 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()观察者

因此,如果所有这些大致正确,我的问题是在步骤d上(。我的代码正在抛出一个例外:

java.lang.IllegalStateException: ArgumentMatchers.any(T::class.java) must not be null

所以我猜onChanged被调用为零值。我真的不明白这里到底发生了什么 - 在步骤c中调用 setLogin()触发用户switchmap live数据,又调用 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 = 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()的无效调用,理想情况下可以指定匹配类。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 = Resource.success(fooUser)
    foo.value = fooValue
    //Ensure setting this did indeed trigger our live data
    verify(observer).onChanged(fooValue)
    reset(observer)

您正确理解了测试。如果您显示您的代码,所有人都会更清楚地帮助您。

但是让我猜测:您正在尝试将Java代码转换为Kotlin代码。在某个时候,您可能在代码中有mockito.any((来模拟行为或内部验证表达式。不可能仅与Kotlin一起使用任何((。关于如何解决此问题有一个线程:是否可以在Kotlin中使用Mockito?

您对测试操作的评估是广义上正确的,除了它不一定意味着用null调用onChanged

详细介绍了我的经验,Kotlin中有一系列oversions of Mockito,可以更好地与Kotlin语言兼容,并改善了测试语法。

https://github.com/nhaarman/mockito-kotlin

我们使用这些,我推荐它们。但是,我们发现,如果我们不谨慎进口,并且我们导入:

import org.mockito.ArgumentMatchers.any

而不是:

import com.nhaarman.mockito_kotlin.any

因此使用一组混合的Java类&amp;Kotlin扩展,然后我们看到您注意到的ArgumentMatchers.any(T::class.java) must not be null错误。

鉴于您正在使用MockitoKotlinHelpers,因此您的情况很可能很相似。

最新更新