Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JVM unit testing with Mockito for testing Retrofit2 and RxJava for network requests

Android Studio 2.3 RC 1

I am using the MVP architecture and want to run JVM unit tests.

In my Model I am using Retrofit2 and RxJava to fetch movies from a API. I want to test the function getPopularMovies(...) However, this function will make a call to the webserver. However, in the test I want to mock this somehow and just test the onSuccess() and onFailure() methods are called.

My model class looks like this snippet only to keep it short:

public class MovieListModelImp implements MovieListModelContract {

    @Override
    public void getPopularMovies(PopularMovieResultsListener popularMovieResultsListener) {
        mSubscription = mMovieAPIService.getPopular(Constants.MOVIES_API_KEY)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<Results>() {
            @Override
            public void onCompleted() {
                Timber.d("onCompleted");
            }

            @Override
            public void onError(Throwable e) {
                Timber.e(e, "onError");
                popularMovieResultsListener.onFailure(e.getMessage());
            }

            @Override
            public void onNext(Results results) {
                Timber.d("onNext %d", results.getResults().size());
                popularMovieResultsListener.onSuccess(results);
            }
        });
    }
}

And the interface:

public interface MovieListModelContract {
    interface PopularMovieResultsListener {
        void onFailure(String errorMessage);
        void onSuccess(Results popularMovies);
    }
    void getPopularMovies(PopularMovieResultsListener popularMovieResultsListener);
}

My problem I am trying to solve is how can I use Mockito to test the getPopularMovies without actually calling the network service? I just want to test that: popularMoviesResultsListener.onFailure(e.getMessage()) will be called on failure to get movies and popularMovieResultsListener.onSuccess(results); will be called on success when movies are recieved

I have a test like this but I am not sure if this is correct:

@Test
public void shouldDisplaySuccessWhenNetworkSucceeds() {
    /* Results is the movie results class that is returned */
    Results results = new Results();

    /* Mock the listener */
    MovieListModelContract.PopularMovieResultsListener mockPopularMoviesResultsListener =
            Mockito.mock(MovieListModelContract.PopularMovieResultsListener.class);

    /* Real instance of the model */
    MovieListModelImp movieListModelImp = new MovieListModelImp();

    /* Call getPopularMovies with mock listener - However, this will still make a real network request */
    movieListModelImp.getPopularMovies(mockPopularMoviesResultsListener);

    /* Verify - but I think I have got this all wrong */
    verify(mockPopularMoviesResultsListener, times(1)).onSuccess(results);
}

So my problem is how can I mock a call to a network request and test the expected onSuccess() and onFailure() is working correctly?

like image 572
ant2009 Avatar asked Feb 24 '17 18:02

ant2009


People also ask

How to test RxJava and retrofit?

How to test RxJava and Retrofit 1 Get rid of the static call - use dependency injection 2 Create the test class 3 Mock and test More ...

What are unit tests in Java?

Unit Tests are test cases which runs on JVM, used to find bugs in code at the early stages, it is not for the product, but for the developer to write good bug-free code in his lifetime. It is base for TDD, and easy to write, maintain and understand.

What is the difference between mock and Mockito?

In contrast, a Mock object will by default just return null for any method that is called. At the beginning of the post we had two main concerns with testing complex objects: failures in the test can be caused by poor implementation from an object we inject. With Mockito we were able to overcome both of these issues.

How to test REST API in an Android app in JUnit?

@IgorGanapolsky You can't directly test REST APIs in an Android app in JUnit if you use any class that references the Android SDK, as JUnit doesn't load the Android classes.


1 Answers

The idea is to user TestSubscriber to assert in unit tests.

Return the Observable from Retrofit instead of void

(Note that I have removed the listener PopularMovieResultsListener as you are using RxJava. With RxJava you can subscribe to the returned Observable and use onNext(), onComplete(), onError() instead.)

public class MovieListModelImp implements MovieListModelContract {

    @Override
    public Observable<Results> getPopularMovies() {
        /** Return the Observable from Retrofit. */
        return mMovieAPIService.getPopular(Constants.MOVIES_API_KEY);
}

Use TestSubscriber in your unit tests to assert

@Mock
MovieAPIService mMovieAPIService

@Test
public void shouldDisplaySuccessWhenNetworkSucceeds() {

    /* Results is the movie results class that is returned */
    Results expectedResults = new Results();


    MovieListModelImp movieListModelImp = new MovieListModelImp();
    //Mock mMovieAPIService which is the actual network call
    when(mMovieAPIService.getPopular(any(String.class)).thenReturn(Observable.just(results));

    Observable<Results> actualResultsObservable = movieListModelImp.getPopularMovies();
    TestObserver<Results> testObserver = actualResultsObservable.test();
    testObserver.assertSubscribed();

    testObserver.assertResult(expectedResults);

    //verify
    verify(mMovieAPIService, times(1)).getPopular(any(String.class));

}
like image 67
Pravin Sonawane Avatar answered Sep 27 '22 18:09

Pravin Sonawane