簡體   English   中英

使用Mockito進行JVM單元測試,以測試Retrofit2和RxJava的網絡請求

[英]JVM unit testing with Mockito for testing Retrofit2 and RxJava for network requests

Android Studio 2.3 RC 1

我正在使用MVP架構並希望運行JVM單元測試。

在我的模型中,我使用Retrofit2和RxJava從API獲取電影。 我想測試函數getPopularMovies(...)但是,這個函數會調用webserver。 但是,在測試中我想以某種方式模擬這個並且只是測試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);被調用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()是否正常工作?

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

從Obtrofit返回Observable而不是void

(請注意,我已刪除的監聽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));

}

我已經設法在這里完成我的答案。 我不確定這是否是最好的方式,希望其他人可能會發表評論。

對於設置:

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

我舉了一個成功案例的例子,作為一個例子。 但是,因為一切似乎都可行。 我只是想知道是否有更好的方法,或者我做了什么錯誤?

提前謝謝了

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM