Проблема с PublishSubject и TestScheduler, элемент не отправляется

У меня возникла проблема с предметами и TestSchedulers. Мои тесты проходят, если я использую планировщик Trampoline, но по какой-то причине они не проходят, если я использую TestScheduler.

Вот мой образец теста и соответствующие классы.

@RunWith(MockitoJUnitRunner::class)
class DemoViewModelTest  {


    //Error Mocks
    private val actionsStream: PublishSubject<DemoContract.ViewEvent> = PublishSubject.create()

    private lateinit var viewModel: DemoViewModel

    private val handler = mock(DemoContract.Handler::class.java)

    @Before
    fun setup() {
        viewModel = DemoViewModel(schedulersProvider, handler)
        viewModel.viewEventsStream = actionsStream
    }

    @Test
    fun testUpdateCounter() {
        actionsStream.onNext(DemoContract.ViewEvent.UpdateClick)
        testScheduler.triggerActions()
        verify(handler).onUpdate()

    }


    protected var testScheduler = TestScheduler()

    protected var schedulersProvider: SchedulersProvider = object : SchedulersProvider() {
        override fun mainThread(): Scheduler {
            return testScheduler
        }

        override fun io(): Scheduler {
            return testScheduler
        }

        override fun computation(): Scheduler {
            return testScheduler
        }

        override fun newThread(): Scheduler {
            return testScheduler
        }

        override fun trampoline(): Scheduler {
            return testScheduler
        }

        override fun single(): Scheduler {
            return testScheduler
        }
    }
}

И мой класс ViewModel

class DemoViewModel (val schedulersProvider: SchedulersProvider, val handler:DemoContract.Handler) : DemoContract.ViewModel() {

    var viewEventsStream: Observable<DemoContract.ViewEvent>? = null
        set(value) {
            field = value
            subscribeToViewEvents()
        }

    private fun subscribeToViewEvents() {
        viewEventsStream?.let {
            it.subscribeOn(schedulersProvider.io())
                .observeOn(schedulersProvider.mainThread())
                .subscribe(object:Observer<DemoContract.ViewEvent>{
                    override fun onComplete() {

                    }

                    override fun onSubscribe(d: Disposable) {
                    }

                    override fun onNext(t: DemoContract.ViewEvent) {
                        onViewEvent(t)
                    }

                    override fun onError(e: Throwable) {
                    }

                })

        }
    }
     fun onViewEvent(event: DemoContract.ViewEvent) {
        when (event) {
            is DemoContract.ViewEvent.UpdateClick -> {
               handler.onUpdate()
            }
        }
    }

}

и мой класс контракта

interface DemoContract {
        abstract class ViewModel

        sealed class ViewEvent {
            object UpdateClick : ViewEvent()
        }

        interface Handler{
            fun onUpdate()
        }
    }

Несколько слов об этом, если я заменю

viewModel.viewEventsStream = actionsStream

с участием

viewModel.viewEventsStream = Observable.just(DemoContract.ViewEvent.Update)

этот тест проходит.

Может кто-нибудь пролить свет на это, заранее спасибо. Вот минимальный работоспособный проект

Что произойдет, если вы вставите оператор регистрации перед оператором subscribeOn()? Попробуйте .doOnNext( v -> logger.debug(v.toString()) -

Bob Dalgleish 28.11.2018 20:10

Его не вызывают. Вот простой воспроизводимый проект git, если хотите. gitlab.com/anvith/testissues

humblerookie 29.11.2018 08:05

Перед вызовом actionsStream.hasObservers проверьте, верно ли значение onNext.

akarnokd 29.11.2018 08:44

subscribeToViewEvents когда-нибудь вызывается?

akarnokd 29.11.2018 08:46

@akarnokd Для actionsStream.hasObservers установлено значение false, событие subscribeToViewEvents вызывается в результате viewModel.viewEventsStream = actionsStream. Я проверил, что метод onSubscribe вызывается с объектом подписки. Также извиняюсь за кросс-пост.

humblerookie 29.11.2018 08:58
actionsStream.hasObservers - false <- проблема в том, что вы не оформляете подписку вовремя, поэтому некому получить товар.
akarnokd 29.11.2018 09:17

Спасибо, что указали мне правильное направление :)

humblerookie 30.11.2018 07:24
2
7
1 198
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Большое спасибо @akarnokd за то, что указал мне в правильном направлении. Оказывается, это состояние гонки между подписчиком добавляемого PublishSubject (вызов subscribeActual) и вызовом onNext, который у меня был триггер в моем тесте. Вызов onNext возвращается до того, как произойдет первое.

Решение состоит в том, чтобы явно вызвать triggerActions дважды, один раз сразу после подписки и один раз после выпуска.

Измените это

    @Before
    fun setup() {
        viewModel = DemoViewModel(schedulersProvider, handler)
        viewModel.viewEventsStream = actionsStream
    }

К

    @Before
    fun setup() {
        viewModel = DemoViewModel(schedulersProvider, handler)
        viewModel.viewEventsStream = actionsStream
        testScheduler.triggerActions()
    }

Другие вопросы по теме