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

  • Utilizando el valor inicial de observable en suscripción junto con el mapeado
  • Lista mutable de Obersable <Object> espere hasta que todos terminen y combínelos en una list <Object>
  • OnComplete nunca se llamó con toSortedList () y groupBy ()
  • Cómo usar la class ContextWrapper con subscribeWith
  • El método de callback a menudo para reenviar el evento a Observable?
  • Extender RxJava Observable en Kotlin con eliminación adecuada
  • Cómo pasar nulo a un Observable con tipo anulable en RxJava 2 y Kotlin
  • Cómo get el valor del ObservableField en android
  • No se llama a ninguno de los suscriptores de RxJava onNext / onError / onComplete al encadenar Observables creado desde Observable.create ()
  • JavaRX: cómo devolver el valor en caching inmediatamente y en paralelo hacer una request de networking
  • Asunto de RxJava: escuche el tipo diferente que el que emite