Solo la primera testing pasa con TestScheduler cuando se ejecutan varias testings (Kotlin)

Estoy tratando de probar mi presentador en una architecture MVP utilizando RxJava en Kotlin. TestScheduler una regla de testing para sustituir los progtwigdores habituales con un TestScheduler para poder probar las requestes asíncronas:

 class TestSchedulerRule : TestRule { val testScheduler = TestScheduler() override fun apply(base: Statement, d: Description): Statement { return object : Statement() { @Throws(Throwable::class) override fun evaluate() { RxJavaPlugins.setInitIoSchedulerHandler { testScheduler } RxJavaPlugins.setInitComputationSchedulerHandler { testScheduler } RxJavaPlugins.setInitNewThreadSchedulerHandler { testScheduler } RxJavaPlugins.setInitSingleSchedulerHandler { testScheduler } RxAndroidPlugins.setInitMainThreadSchedulerHandler { testScheduler } try { base.evaluate() } finally { RxJavaPlugins.reset() RxAndroidPlugins.reset() } } } } } 

Esta es mi class de testing. El problema es que cada testing está pasando, si las ejecuto por separado, pero si ejecuto todo el set, solo pasa el primero. Debería haber un problema con TestScheduler , porque si uso un progtwigdor inmediato, funciona.

 class UserListPresenterTest { @Rule @JvmField val testSchedulerRule = TestSchedulerRule() @Mock lateinit var mockGetUsers: GetUsers @Mock lateinit var mockView: UserListView lateinit var userListPresenter: UserListPresenter @Before fun setup() { MockitoAnnotations.initMocks(this) userListPresenter = UserListPresenter(mockGetUsers) } @Test fun testGetUsers_errorCase_showError() { // Given val error = "Test error" val single: Single<List<UserViewModel>> = Single.create { emitter -> emitter?.onError(Exception(error)) } // When `when`(mockGetUsers.execute(Mockito.anyInt(), Mockito.anyBoolean())).thenReturn(single) userListPresenter.attachView(mockView) userListPresenter.getUsers() testSchedulerRule.testScheduler.triggerActions() // Then verify(mockView).hideLoading() verify(mockView).showError() } @Test fun testGetUsers_successCaseFirstPage_clearList() { // Given val users = listOf(UserViewModel(1, "Name", 1000, "")) val single: Single<List<UserViewModel>> = Single.create { emitter -> emitter?.onSuccess(users) } // When `when`(mockGetUsers.execute(Mockito.anyInt(), Mockito.anyBoolean())).thenReturn(single) userListPresenter.attachView(mockView) userListPresenter.getUsers() testSchedulerRule.testScheduler.triggerActions() // Then verify(mockView).clearList() } } 

Aquí está el error que recibo en la console:

 Wanted but not invoked: mockView.hideLoading(); -> at com.example.tamaskozmer.kotlinrxexample.UserListPresenterTest.testGetUsers_errorCase_showError(UserListPresenterTest.kt:57) Actually, there were zero interactions with this mock. at com.example.tamaskozmer.kotlinrxexample.UserListPresenterTest.testGetUsers_errorCase_showError(UserListPresenterTest.kt:57) 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 com.example.tamaskozmer.kotlinrxexample.TestSchedulerRule$apply$1.evaluate(TestSchedulerRule.kt:28) at org.junit.rules.RunRules.evaluate(RunRules.java:20) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68) at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51) at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70) 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.application.AppMainV2.main(AppMainV2.java:131) 

Clase UserListPresenter

 class UserListPresenter(private val getUsers: GetUsers) : BasePresenter<UserListView>() { private val offset = 5 private var page = 1 private var loading = false fun getUsers(forced: Boolean = false) { loading = true if (forced) { resetPaging() } getUsers.execute(page, forced) .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) .subscribe({ users -> loading = false if (page == 1) { view?.clearList() } view?.addUsersToList(users) view?.hideLoading() page++ }, { loading = false view?.showError() view?.hideLoading() }) } private fun resetPaging() { page = 1 } fun onScrollChanged(lastVisibleItemPosition: Int, totalItemCount: Int) { if (!loading) { if (lastVisibleItemPosition >= totalItemCount - offset) { getUsers() } } if (loading && lastVisibleItemPosition >= totalItemCount) { view?.showLoading() } } } 

Solo corrigió el mismo problema. Debe definir los progtwigdores y no especificarlos en la regla. RxAndroidPlugins mantendrá la primera instancia de TestScheduler después de que TestRule.apply ponga otra instancia para una nueva testing.

Poner los controlleres del progtwigdor de configuration en objects complementarios ayudará

 companion object { val testScheduler: TestScheduler init { testScheduler = TestScheduler() RxAndroidPlugins.setInitMainThreadSchedulerHandler { _ -> testScheduler } RxJavaPlugins.setInitComputationSchedulerHandler { _ -> testScheduler } RxJavaPlugins.setInitIoSchedulerHandler { _ -> testScheduler } } } 

Puede considerar el uso de Inyección de progtwigdor con el patrón de Estrategia. Por favor revisa estos enlaces;

https://medium.com/@burakeregar/kotlin-rxjava-dagger2-mvp-clean-architecture-easy-unit-testing-5a756c4f2c93

https://github.com/burakeregar/KotlinRxMvpArchitecture

  • Android: uso de generics para tener 1 llamada de actualización de RxJava que devuelve varios types usando la misma interfaz
  • Causado por: rx.exceptions.MissingBackpressureException
  • MissingMethodInvocationException probando una class abierta en Kotlin
  • Kotlin y RxJava2 zip operator - Ninguna de las siguientes funciones puede invocarse con los arguments suministrados
  • Observable para reutilizar ejecuciones de operadores
  • Cómo recordar el estado con los operadores de rebashs en RxJava2
  • Kotlin cuádruple, quíntuple, etc. para desestructurar
  • Crear un observable común que evite múltiples llamadas
  • Adaptador personalizado de Moshi con RxAndroid & Retrofit & Kotlin
  • Kotlin: cómo henetworkingar del suscriptor de RxJava
  • No se puede hacer que la API invoque urlfetch.Fetch en un hilo que no sea el hilo de request original ni un hilo creado por ThreadManager