3

I'm trying to migrate my app to work with RxJava. I already use Retrofit and therefore I'm trying to use a Retrofit interface which methods return Observables. However I'm now having issues with coding tests against it, as I can't get the Observable to run on the main thread; I'm trying to use Scheduler.immediate() for it. It seems that Retrofit doesn't allow to override it's behaviour, which makes totally sense for the real execution flow, but it makes testing very difficult. As I've just started with RxJava + Retrofit I just hope I'm doing something wrong instead.

Below is what the code looks like:

@Test
public void shouldCompleteRequest() {
    SomeRestRequest request = new SomeRestRequest(arg1, arg2);
    TestSubscriber<SomeRestResponse> testSubscriber = new TestSubscriber<>();
    new SomeRestCommand(mRestApi,
            arg1, arg2
            Schedulers.immediate(),
            Schedulers.immediate(),
            mMockEventBus).execute(request, testSubscriber);
    testSubscriber.assertCompleted();
}

where

public void execute(T request, Observer<S> observer) {
    getCommand(request)
        .observeOn(mObserveOnScheduler) //The test injects Schedulers.immediate()
        .subscribeOn(mSubscribeOnScheduler) //The test injects Schedulers.immediate()
        .subscribe(observer);
}

,

@Override
protected Observable<SomeRestResponse> getCommand(SomeRestRequest request) {
    return mRestApi.restCommand(arg1, arg2);
}

and

public interface RestApi {
    @GET("/someEndPoint")
    Observable<SomeRestResponse> restCommand(@Query("arg1") String arg1, @Query("arg2") String arg2);
}
nyarlathotep77
  • 181
  • 1
  • 9
  • "Retrofit doesn't allow to override it's behavior" - Can you elaborate it more? It's hard to say without seeing code. – Aaron He Jan 23 '16 at 01:16
  • @AaronHe, it seems impossible to run Retrofit Observable on the main thread, such as I'd like to do for my tests by using Scheduler.immediate(). – nyarlathotep77 Jan 23 '16 at 09:12

1 Answers1

5

If you modify your test to add testSubscriber.awaitTerminalEvent();, then your test will wait for the call to complete and hence the test will pass. You will still have to do an assertCompleted() as the terminal event can be either of successful completion or error.

@Test
public void shouldCompleteRequest() {
    SomeRestRequest request = new SomeRestRequest(arg1, arg2);
    TestSubscriber<SomeRestResponse> testSubscriber = new TestSubscriber<>();
    new SomeRestCommand(mRestApi,
            arg1, arg2
            Schedulers.immediate(),
            Schedulers.immediate(),
            mMockEventBus).execute(request, testSubscriber);

    testSubscriber.awaitTerminalEvent(); // add this line here
    testSubscriber.assertCompleted();
}

I looked up the source code of Retrofit 1.9.0 and as per RxSupport class, the call is always executed in a separate thread provided by the httpExecutor. Hence using Schedulers.immediate() did not cause the call to happen in the main thread.

Praveer Gupta
  • 3,940
  • 2
  • 19
  • 21
  • Thank you for your detailed answer.That's exactly what I'm doing, but my test completes before the actual REST request. In fact I can't see any of the Retrofit logs while the test is running. – nyarlathotep77 Jan 23 '16 at 09:10
  • I can actually see the Retrofit logs and the request gets executed correctly, but I still get: `java.lang.AssertionError: Not completed!` – nyarlathotep77 Jan 23 '16 at 10:44
  • This would happen when you would have used the `subscribeOn(scheduler)` method on the observable in the method you are trying to test. This causes the test and Retrofit to run on different threads. You have two options: first is to inject the Scheduler, so that you can replace the Scheduler with Schedulers.immediate() in the test. If you have already used something like `subscribeOn(Schedulers.io())`, then you will have to use the second way of using RxJavaPlugins and RxJavaSchedulersHook. – Praveer Gupta Jan 23 '16 at 10:47
  • @praver09, thank you. I already pass the schedulers via DI. In my test configuration both subscribeOn() and observeOn() receive a Schedulers.immediate(). I'll try to share some code later. – nyarlathotep77 Jan 23 '16 at 12:02
  • @nyarlathotep77 - I have updated my answer based on the information that you have provided. Hope it helps. – Praveer Gupta Jan 25 '16 at 03:07
  • good call @praveer09. I had a look at Retrofit 1.9.0 code last night and I realised the same. Thanks a lot! Looking at Retrofit 2.0.0 I suspect this behaviour might have changed, but I'll have to verify that. – nyarlathotep77 Jan 25 '16 at 07:04