19

Using Retrofit 1 we used to mock web services and simulate network latency as following:

MockRestAdapter mockRestAdapter = MockRestAdapter.from(restAdapter);
return mockRestAdapter.create(MyService.class, new MyServiceMock());

Where MyService is the service Interface (returning responses as Rx Observables), and MyServiceMock is a class that implements this interface.

In Retrofit 2.0.0-beta3 there is a brand new mocking system (see: https://github.com/square/retrofit/pull/1343) that is not yet documented. When trying to to something similar I get:

MockRetrofit mockRetrofit = new MockRetrofit.Builder(retrofit).build();
BehaviorDelegate<AuthService> delegate = mockRetrofit.create(MyService.class);

How do I forward calls to MyServiceMock?

Winter
  • 3,894
  • 7
  • 24
  • 56
clemp6r
  • 3,665
  • 2
  • 26
  • 31

1 Answers1

27

Suppose the interface definition is as below:

public interface MyService {
    @GET("/name")
    rx.Observable<String> name();
}

Calls will be forwarded to MyServiceMock using BehaviorDelegate. The BehaviorDelegate applies NetworkBehavior to the responses created by the mock. Note that the mock implementation below takes the BehaviorDelegate as a constructor argument and uses it to return the response.

public class MyServiceMock implements MyService {
    private final BehaviorDelegate<MyService> delegate;

    public MyServiceMock(BehaviorDelegate<MyService> delegate) {
        this.delegate = delegate;
    }

    public Observable<String> name() {
        return delegate.returningResponse("test").name();
    }
}

A sample of a test using the mock service is as shown below:

public class MyServiceTest {

    private final NetworkBehavior behavior = NetworkBehavior.create();
    private final rx.observers.TestSubscriber<String> testSubscriber = TestSubscriber.create();
    private MyService mockService;

    @Before
    public void setUp() throws Exception {
        Retrofit retrofit = new Retrofit.Builder()
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .baseUrl("http://example.com").build();

        MockRetrofit mockRetrofit = new MockRetrofit.Builder(retrofit)
                .networkBehavior(behavior).build();

        final BehaviorDelegate<MyService> delegate = mockRetrofit.create(MyService.class);

        mockService = new MyServiceMock(delegate);
    }

    @Test
    public void testSuccessResponse() throws Exception {
        givenNetworkFailurePercentIs(0);

        mockService.name().subscribe(testSubscriber);

        testSubscriber.assertValue("test");
        testSubscriber.assertCompleted();
    }

    @Test
    public void testFailureResponse() throws Exception {
        givenNetworkFailurePercentIs(100);

        mockService.name().subscribe(testSubscriber);

        testSubscriber.assertNoValues();
        testSubscriber.assertError(IOException.class);
    }

    private void givenNetworkFailurePercentIs(int failurePercent) {
        behavior.setDelay(0, MILLISECONDS);
        behavior.setVariancePercent(0);
        behavior.setFailurePercent(failurePercent);
    }
}
Praveer Gupta
  • 3,940
  • 2
  • 19
  • 21
  • 1
    Ok, so if I understand I will have to rewrite all my mocks to add the the following boilerplate `delegate.returning(myMockedValue).myMethod();`. I'm not sure this is an improvement compared to Retrofit 1. – clemp6r Jan 12 '16 at 13:53
  • 1
    Is there a reason to ever write `delegate.returning(Calls.response(something))` if you can call `delegate.returningResponse(something))`, which does the "Calls.response()" part for you? – Singed Apr 15 '16 at 14:51
  • 1
    @Singed - You are right. There is no reason to write the long form in this case. `returningResponse` can be used. However where this can be useful is when you want to send failed responses from your mock. Example `delegate.returning(Calls.failure(new IOException("some error")))`. – Praveer Gupta Apr 16 '16 at 02:37
  • I believe BehaviorDelegate uses reflection for its interface. Isn't it a bit convoluted? I am wondering if there is a more straightforward/simpler way to test web services. – IgorGanapolsky May 04 '16 at 21:32
  • Cannot resolve subscribe method. Where did that came from? – Lance Jun 15 '17 at 03:20
  • 1
    @LanceContreras - I believe you are asking about the subscribe() method call on name(). As name() is RxJava Observable, I am calling subscribe() method on it. – Praveer Gupta Jun 15 '17 at 06:53
  • @PraveerGupta subscribe() requires no parameters. I also have the same problem as Lance Conteras – nutella_eater Jul 01 '17 at 19:04
  • If you're using RxJava 2, I'm not sure but you *might* need to use a TestObserver instead of a TestSubscriber depending on your situation – the_new_mr Sep 06 '17 at 11:12
  • This works fine for me when the service is returning an Observable but I can't get it to work at all if I'm returning Observable. Any tips? – the_new_mr Sep 06 '17 at 11:44