2017-02-24 64 views
7
Android Studio 2.3 RC 1 

我正在使用MVP體系結構並希望運行JVM單元測試。使用Mockito進行JVM單元測試以測試Retrofit2和RxJava的網絡請求

在我的模型中,我使用Retrofit2和RxJava從API獲取電影。我想測試這個函數getPopularMovies(...)但是,這個函數會調用web服務器。但是,在測試中,我想以某種方式模擬這種方式,只是測試onSuccess()onFailure()方法。

我的模型類看起來像這個片段只是爲了保持短:

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

和接口:

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

我的問題我試圖解決的是怎麼用的Mockito測試getPopularMovies沒有實際調用網絡服務?我只是想測試: popularMoviesResultsListener.onFailure(e.getMessage())將被要求未能獲得電影 和 popularMovieResultsListener.onSuccess(results);會成功時,電影收到

我有一個這樣的測試,但我不知道這是否是正確的被稱爲:

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

所以我的問題是我怎麼能嘲笑調用一個網絡請求並測試了預期的onSuccess()和onFailure處()是否正常工作?

回答

1

我已成功地完成在這裏我的答案。我不確定這是否是最好的方式,希望其他人能夠發表評論。

對於設置:

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

而且推倒

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

我的服務API

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

嘲笑

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

我的測試

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

我給出了一個成功案例的例子,它可以作爲例子工作。但是,因爲一切似乎都很好。我只是想知道有沒有更好的方法去做,或者我做了什麼錯誤?

非常感謝提前

+0

我不認爲你已經按照單元測試你的課程所需要的那樣嘲笑了真實的網絡請求。我試圖在我的回答中解決這個問題:) –

+0

@PravinSonawane這是我的錯誤。我粘貼了錯誤的測試代碼片段。我現在已經更新了我的答案。這裏我使用when(...),thenReturn。模擬MovieAPIService.getPopular(..)以返回正確的結果。然後我驗證正確的onFailure()和onSuccess()被稱爲正確的次數。這只是測試業務邏輯,而不是對網絡進行真正的呼叫。測試獨立於任何外部依賴。 – ant2009

2

這個想法是用戶TestSubscriber在單元測試中斷言。

返回從改造,而不是可觀測空白

(請注意,我已刪除的監聽PopularMovieResultsListener與您使用RxJava隨着RxJava您可以訂閱返回的觀察到的,使用onNext()onComplete()onError()代替。)

public class MovieListModelImp implements MovieListModelContract { 

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

使用在單元測試中TestSubscriber斷言

@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

謝謝我會試一試。但是,我想讓我的聽衆成爲我設計的一部分。 – ant2009

+0

我已經設法回答我自己的問題,並張貼在這裏。它適用於成功和失敗的情況。我嘲笑了我的聽衆。如果你認爲我能做的更好,也許你可以發表評論。 – ant2009