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 podemos implementar Observable.flatMapCompletable?
  • Cómo recordar el estado con los operadores de rebashs en RxJava2
  • ¿Cómo puedo agregar de manera condicional una operación asincrónica en medio de una transmisión de RxJava?
  • Operador RxJava para el método de conmutación
  • Mockito querido pero no invocado
  • Se llama a Android RxJava onNext incluso cuando se anula la suscripción del observador
  • Kotlin: cómo henetworkingar del suscriptor de RxJava
  • Cómo contar el time de ejecución de lo observable
  • No se llama a ninguno de los suscriptores de RxJava onNext / onError / onComplete al encadenar Observables creado desde Observable.create ()
  • Método comodín de Kotlin
  • Transformaciones de datos con RxJava en Kotlin