Maxim Blumental Maxim Blumental - 3 months ago 28
Android Question

How to choose the right scheduler for unit-tests

I have the following code to test:

public class MyInteractor implements Interactor<String, Boolean> {

private MyRepository repository;

@Inject
public MyInteractor(MyRepository repository) {
this.repository = repository;
}

@Override
public Observable<Boolean> createObservable(final String s) {
return Observable.create(new Observable.OnSubscribe<Boolean>() {
@Override
public void call(Subscriber<? super Boolean> subscriber) {

Boolean response = repository.call(s);

subscriber.onNext(response);
subscriber.onCompleted();
}
})
.subscribeOn(Schedulers.io());
}
}


I wrote the following unit test:

@RunWith(MockitoJUnitRunner.class)
public class MyInteractorTest {

@Mock
MyRepository repository;

@InjectMocks
MyInteractor interactor;

@Before
public void init() {
when(repository.call(any(String.class)))
.thenReturn(true);
}

@Test
public void myTest() throws Exception {

Observable<Boolean> interactorObservable =
interactor.createObservable("hello");
TestSubscriber<Boolean> subscriber = TestSubscriber.create();

interactorObservable.subscribe(subscriber);

subscriber.assertNoErrors();

subscriber.assertCompleted();
subscriber.assertReceivedOnNext(singletonList(true));
}
}


When I run the unit test it fails on
subscriber.assertCompleted()
. However if I remove
subscribeOn(Schedulers.io())
in
MyInteractor.createObservable()
everything works fine.

Is there a way to make the test run to completion and not to remove the line
subscribeOn(Schedulers.io())
?

Answer

This was because myTest wasn't aware of there's stuff running on another thread, so it continues to run assertions before Observable returns.

Use RxJavaHooks to instrument IO Scheduler returning Immediate Scheduler in unit tests.

For example: RxJavaHooks.setOnIOScheduler(scheduler -> Schedulers.immediate())