【问题标题】:JVM unit testing with Mockito for testing Retrofit2 and RxJava for network requests使用 Mockito 进行 JVM 单元测试,用于测试 Retrofit2 和 RxJava 的网络请求
【发布时间】:2017-07-15 16:48:19
【问题描述】:
Android Studio 2.3 RC 1

我正在使用 MVP 架构并希望运行 JVM 单元测试。

在我的模型中,我使用 Retrofit2 和 RxJava 从 API 获取电影。我想测试函数getPopularMovies(...) 但是,这个函数会调用网络服务器。但是,在测试中,我想以某种方式模拟它,只测试调用了 onSuccess()onFailure() 方法。

我的模型类看起来像这样 sn-p 只是为了保持简短:

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() 是否正常工作?

【问题讨论】:

    标签: android mockito rx-java retrofit2


    【解决方案1】:

    这个想法是让用户TestSubscriber 在单元测试中断言。

    从 Retrofit 返回 Observable 而不是 void

    (请注意,当您使用 RxJava 时,我已经删除了侦听器 PopularMovieResultsListener。使用 RxJava,您可以订阅返回的 Observable 并改用 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));
    
    }
    

    【讨论】:

    • 谢谢,我会试一试。但是,我想将我的听众作为我设计的一部分。
    • 我已经设法回答了我自己的问题并在此处发布。它适用于成功和失败的案例。我嘲笑了我的听众。如果您认为我可以做一些更好的事情,也许您可​​以发表评论。
    【解决方案2】:

    我已经在这里完成了我的回答。我不确定这是否是最好的方法,希望其他人可以发表评论。

    设置:

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

    我在这里给出了一个成功案例的例子,它可以作为一个例子。但是,一切似乎都正常。我只是想知道有没有更好的方法,或者我所做的有什么错误?

    在此先感谢

    【讨论】:

    • 我认为您没有像对您的课程进行单元测试那样模拟真实的网络请求。我试图在我的回答中解决这个问题:)
    • @PravinSonawane 那是我的错误。我粘贴了错误的代码 sn-p 进行测试。我现在已经更新了我的答案。这里我使用 when(...).thenReturn。模拟 MovieAPIService.getPopular(..) 以返回正确的结果。然后我验证正确的 onFailure() 和 onSuccess() 已被调用了正确的次数。这只是测试业务逻辑,而不是对网络进行任何真正的调用。测试独立于任何外部依赖项。
    猜你喜欢
    • 2017-09-19
    • 2016-06-15
    • 1970-01-01
    • 1970-01-01
    • 2019-04-27
    • 1970-01-01
    • 2023-03-28
    • 2020-02-22
    相关资源
    最近更新 更多