Search code examples
androidmockitoretrofitrx-java

Mockito MockApi returns null


PROBLEM

I'm trying to test my logIn() method with Mockapi. Unfortunately every time I try to subscribe on my Observer I get null object reference because logIn() returns null.

I don't understand where there problem is. Why my mocked api does not return the data?

CODE

###logInTest###

@RunWith(MockitoJUnitRunner.class) 
@LargeTest
public class LogInTest {

@Mock
MockAPI mockAPI;

@Before
public void setUp() {
    MockitoAnnotations.initMocks(this);
}

@Test
public void performValidLogIn() throws Exception {
    LoginResponse expectedResponse = new LoginResponse();
    expectedResponse.setToken("RANDOM TOKEN");

    TestSubscriber<LoginResponse> testSubscriber = new TestSubscriber<>();
    Observable<LoginResponse> observable = mockAPI.logIn(new LogInData("TestLogin", "TestPassword"));
    observable.subscribe(testSubscriber);
    testSubscriber.assertNoErrors();
    testSubscriber.assertReceivedOnNext(Collections.singletonList(expectedResponse));
}
}

###MockAPI###

public class MockAPI implements API {

@Override
public Observable<LoginResponse> logIn(@Body LogInData logInData) {
    return Observable.create(new Observable.OnSubscribe<LoginResponse>      () {
        @Override
        public void call(Subscriber<? super LoginResponse> subscriber) {
            LoginResponse testData = new LoginResponse();
            testData.setToken("RANDOM TOKEN");
            subscriber.onNext(testData);
            subscriber.onCompleted();
        }
    });
}
}

Solution

  • Mockito wraps your target class/interface with a mockable object for you, so there's no need for the MockAPI class. Mockito also creates what is essentially a blank shell around your target class during initialization—you later define behavior piecemeal using the when method.

    In your scenario, by annotating the MockAPI with @Mock, Mockito is mocking your MockAPI instead of your API. The login method is returning null because no behavior has yet been defined through Mockito.

    Change the field to directly mock your API:

    @Mock
    API mockApi;
    

    Then, you would define your logIn method in the test body to return the desired Observable:

    @Test
    public void performValidLogIn() throws Exception {
        LoginResponse expectedResponse = new LoginResponse();
        expectedResponse.setToken("RANDOM TOKEN");
        when(mockApi.logIn(any(LogInData.class))).thenReturn(Observable.just(expectedResponse));
    
        TestSubscriber<LoginResponse> testSubscriber = new TestSubscriber<>();
        Observable<LoginResponse> observable = mockAPI.logIn(new LogInData("TestLogin", "TestPassword"));
        observable.subscribe(testSubscriber);
        testSubscriber.assertNoErrors();
        testSubscriber.assertReceivedOnNext(Collections.singletonList(expectedResponse));
    }
    

    Side note: from the test case you provided (assuming it isn't contrived), I'm under the impression you want to test the behavior of your login method. However, by mocking that method itself, you will actually be testing the mock instead of your production code. Ideally you would want to test a real class that exercises your API's login method and then pass in the mocked API dependency for it to use.