Testing the JVM module with Mockito to test Retrofit2 and RxJava for network requests

Android Studio 2.3 RC 1 

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

In my model, I use Retrofit2 and RxJava to extract movies from the API. I want to check the getPopularMovies(...) function getPopularMovies(...) However, this function will call the web server. However, in the test I want to somehow mock it and just check the onSuccess() and onFailure() methods.

My model class looks like this snippet so that it is 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'm trying to solve is how can I use Mockito to test getPopularMovies without actually calling the network service? I just want to check this: popularMoviesResultsListener.onFailure(e.getMessage()) will be caused by the inability to receive movies as well as popularMovieResultsListener.onSuccess(results); will be called upon to succeed in receiving films

I have such a test, but I'm 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 make fun of a network request call and verify that the expected onSuccess () and onFailure () functions work correctly?

+7
android rx-java mockito retrofit2
source share
2 answers

I managed to fulfill my answer here. I'm not sure if this is the best way and hopefully other people can comment.

For settings:

 @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(MovieListModelImpTest.this); movieListModelContract = new MovieListModelImp(mockMovieAPIService); RxJavaHooks.setOnIOScheduler(new Func1<Scheduler, Scheduler>() { @Override public Scheduler call(Scheduler scheduler) { return Schedulers.immediate(); } }); /* Override RxAndroid schedulers */ final RxAndroidPlugins rxAndroidPlugins = RxAndroidPlugins.getInstance(); rxAndroidPlugins.registerSchedulersHook(new RxAndroidSchedulersHook() { @Override public Scheduler getMainThreadScheduler() { return Schedulers.immediate(); } }); } 

And a breakdown

  @After public void tearDown() throws Exception { RxJavaHooks.reset(); RxAndroidPlugins.getInstance().reset(); } 

My Service API

 @GET("movie/popular") Observable<Results> getPopular(@Query("api_key") String apikey); 

Mocks

 @Mock MovieAPIService mockMovieAPIService; @Mock Observable<Results> mockCall; @Mock ResponseBody responseBody; @Mock MovieListModelContract.PopularMovieResultsListener mockPopularMoviesResultsListener; private MovieListModelContract movieListModelContract; @Captor ArgumentCaptor<Callback<List<Results>>> argumentCaptor; 

My test

  @Test public void shouldDisplaySuccessMessageOnSuccess() { final Results results = new Results(); when(mockMovieAPIService.getPopular(anyString())).thenReturn(Observable.just(results)); movieListModelContract.getPopularMovies(mockPopularMoviesResultsListener); verify(mockPopularMoviesResultsListener, never()).onFailure(anyString()); verify(mockPopularMoviesResultsListener, times(1)).onSuccess(results); } 

I gave an example of a successful case, which works fine as an example. However, everything seems to be working fine. I'm just wondering if there is a better way to do this, or are there any errors in what I did?

Thank you very much in advance

+1
source share

The idea is for a TestSubscriber user TestSubscriber approve in unit tests.

Return Observable from Retrofit instead of void

(Note that I removed the PopularMovieResultsListener listener when you use 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 confirm

 @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)); } 
+2
source share

All Articles