2017-02-24 2 views
7

Ich verwende die MVP-Architektur und möchte JVM-Komponententests durchführen.JVM-Unit-Test mit Mockito zum Testen von Retrofit2 und RxJava für Netzwerkanfragen

In meinem Modell verwende ich Retrofit2 und RxJava, um Filme von einer API zu holen. Ich möchte die Funktion testen getPopularMovies(...) Diese Funktion wird jedoch einen Aufruf an den Webserver vornehmen. Im Test möchte ich das jedoch irgendwie nachspielen und nur die onSuccess() und onFailure() Methoden testen.

Meine Modellklasse sieht aus wie dieser Snippet es kurz zu halten:

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); 
      } 
     }); 
    } 
} 

und die Schnittstelle:

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

Mein Problem, das ich zu lösen versuche, ist, wie kann ich Mockito verwenden zu testen die getPopularMovies ohne tatsächlich den Netzwerkdienst anzurufen? Ich will nur, dass testen: popularMoviesResultsListener.onFailure(e.getMessage()) wird bei einem Fehler genannt werden, um Filme zu bekommen und popularMovieResultsListener.onSuccess(results); auf Erfolg bezeichnet werden, wenn Filme

empfangen werden habe ich einen Test wie diesen, aber ich bin nicht sicher, ob dies richtig ist:

@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); 
} 

Also mein Problem ist, wie kann ich einen Anruf zu einer Netzwerkanfrage und testen Sie die erwartete onSuccess() und onFailure() korrekt funktioniert verspotten?

Antwort

1

Ich habe meine Antwort zu vervollständigen hier verwaltet. Ich bin mir nicht sicher, ob es der beste Weg ist und hoffentlich auch andere Leute kommentieren können.

Für den Aufbau:

@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(); 
     } 
    }); 
} 

Und die Träne nach unten

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

Mein 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; 

Mein 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); 
    } 

ich ein Beispiel für einen Erfolg Fall hier gegeben haben, die als Beispiel ok funktioniert. Wie auch immer scheint alles in Ordnung zu sein. Ich frage mich nur, ob es einen besseren Weg gibt, oder gibt es irgendwelche Fehler in dem, was ich getan habe?

Vielen Dank im Voraus

+0

Ich glaube nicht, dass Sie die echte Netzwerkanforderung verspottet haben, wie es für den Unit-Test Ihrer Klasse erforderlich ist. Ich versuchte, dies in meiner Antwort zu adressieren :) –

+0

@PravinSonawane Das war mein Fehler. Ich habe das falsche Code-Snippet für den Test eingefügt. Ich habe jetzt meine Antwort aktualisiert. Hier verwende ich das when (...). ThenReturn. So verspotten Sie MovieAPIService.getPopular (..), um die korrekten Ergebnisse zurückzugeben. Dann vergewissere ich mich, dass die richtige onFailure() und onSuccess() die richtige Anzahl von Mal aufgerufen wurden. Dies testet nur die Geschäftslogik und führt keine echten Aufrufe an das Netzwerk durch. Der Test ist unabhängig von externen Abhängigkeiten. – ant2009

2

Die Idee ist, Benutzer TestSubscriber in Unit-Tests geltend zu machen.

Rückkehr der beobachtbare von Retrofit statt Leere

(Beachten Sie, dass ich den Hörer abgenommen haben PopularMovieResultsListener wie Sie RxJava verwenden. Mit RxJava Sie auf die zurück beobachtbare abonnieren und verwenden onNext(), onComplete(), onError() statt.)

public class MovieListModelImp implements MovieListModelContract { 

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

Verwenden TestSubscriber in Ihren Unit-Tests zu behaupten

@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)); 

} 
+0

Danke, ich werde das versuchen. Ich möchte jedoch meinen Zuhörer als Teil meines Designs behalten. – ant2009

+0

Ich habe es geschafft, meine eigene Frage zu beantworten und hier gepostet. Es funktioniert für Erfolgs- und Fehlerfälle. Und ich habe meinen Zuhörer verspottet. Vielleicht kannst du einen Kommentar abgeben, wenn du denkst, dass es etwas Besseres gibt, was ich tun kann. – ant2009

Verwandte Themen