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

  • Cómo controlar el flujo sin .flatMap, que rompe una stream reactiva que impide que operadores como distinctUntilChanged trabajen en toda la secuencia
  • Reemplazar callbacks llamados externamente con RxJava
  • RxJava zipCon error IDE en Kotlin con Android Studio 3.0
  • ¿Hay alguna manera de orderar las keys groupBy en rx-java / kotlin?
  • RxJava BehaviorSubject no emite el último elemento?
  • Problemas al analizar datos con RxJava + Kotlin
  • ¿Expresar "súper" generics en los types funcionales de Kotlin?
  • ¿Cómo se coordina una list de ejecuciones Completables con RxJava?
  • Escuchar posts y escribir commands en un flujo observable
  • RxJava usando Kotlin - cómo sincronizar 2 methods asíncronos, refactorizar desde Java
  • Combina Rx Singles en Observables recursivamente