How can I verify this in a unit test?
For example:
Subscription subscribe(Scheduler ioScheduler, Scheduler uiScheduler, Subscriber subscriber) {
return observable.subscribeOn(ioScheduler).observeOn(uiScheduler).subscribe(subscriber);
}
Tried using TestScheduler and TestSubscriber, but could not find a good way to do this.
Use doOnNext and write into a container/field/list the Thread.currentThread and verify if it's name starts RxIOScheduler or similar.
Thanks @akarnokd, but the Schedulers I pass are TestScheduler. Any other suggestions?
TestSchedulers usually run on the main thread (unless you mix them with regular Schedulers or custom threads) and require moving the virtual time manually forward via advanceTimeBy. If you do that and you get the expected emissions in the TestSubscriber then everything worked.
TestScheduler sched = new TestScheduler();
TestSubscriber<Integer> ts = TestSubscriber.create();
Observable.just(1).delay(500, TimeUnit.MILLISECONDS, sched)
.subscribe(ts);
ts.assertNoValues();
ts.assertNoErrors();
ts.assertNotCompleted();
sched.advanceTimeBy(499, TimeUnit.MILLISECONDS);
ts.assertNoValues();
ts.assertNoErrors();
ts.assertNotCompleted();
sched.advanceTimeBy(1, TimeUnit.MILLISECONDS);
ts.assertValue(1);
ts.assertNoErrors();
ts.assertCompleted();
But in this case you have only one scheduler. I have two. What I want to assert is that subscribeOn was used with the ioScheduler (the first param of the method) and the observeOn was triggered with the uiScheduler.
You can't do that, there is no identity of TestSchedulers. The only thing you can check if that if you don't call advanceTimeBy nothing scheduled should happen (like moving the ioScheduler forward but not the uiScheduler).
Running the scheduled actions on ioScheduler and then uiScheduler in that order basically guarantees it. The reverse would fail to notify the subscriber.
A better solution would be to expose two properties on TestScheduler: a total count of scheduled actions and a count of scheduled actions that will run with a call to triggerActions. Then you test could read:
subscribe(io, ui, s);
assertThat(io.scheduledCount()).isEqualTo(1);
assertThat(ui.scheduledCount()).isEqualTo(0);
io.triggerActions();
assertThat(io.scheduledCount()).isEqualTo(0);
assertThat(ui.scheduledCount()).isEqualTo(1);
ui.triggerActions();
s.assertValue(whatever);
Thanks @JakeWharton, the order does make a difference, but still doesn't cover the case where I use the ioScheduler twice in my code.
I guess your second suggestion will solve that better.
So I guess ATM this is not really possible right?
The only way to verify this that I found was to do the following:
subscriber.assertNoValues();
ioScheduler.triggerActions();
subscriber.assertNoValues();
mainScheduler.triggerActions();
subscriber.assertCompleted();
This both verifies the order and the dependency on the both schedulers.
I needed to add another test for use of the mainScheduler twice as well:
subscriber.assertNoValues();
mainScheduler.triggerActions();
subscriber.assertNoValues();
Me again, this time from my personal account. Will close this issue and submit a PR with some suggestions to make this easier to test.
@JakeWharton - I want to find a more explicit solution since scheduledCount is very generic and does not describe the thing I am trying to test. Any suggestions are very welcomed.
I fail to see how a count of scheduled actions would not cover this case. If you used the scheduler twice the count would be 2 instead of 1. Anything beyond that strays too far into white box testing and at that point you can just use Mockito and mock a Scheduler to verify interactions.
I am not saying it doesn't cover this case, it is just a matter of readability of the tests and their intention. But I guess you're right that it will make it a white box testing too much. I will have a deeper look at the TestScheduler and TestSubject to see if I can have something like:
TestScheduler ioScheduler = new TestScheduler("io");
TestScheduler mainScheduler = new TestScheduler("main");
subscibe(testSubject, ioScheduler, mainScheduler, subscriber);
testSubject.assertSubscribeOn("io");
testSubject.assertObserveOn("main");
I am afraid I still have a lot to learn about RxJava so I might be way off with this expectation.
Most helpful comment
Running the scheduled actions on
ioSchedulerand thenuiSchedulerin that order basically guarantees it. The reverse would fail to notify the subscriber.A better solution would be to expose two properties on
TestScheduler: a total count of scheduled actions and a count of scheduled actions that will run with a call totriggerActions. Then you test could read: