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);
将被称为成功
我有这样的测试,但我不确定这是否正确:
@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()是否正常工作?
答案 0 :(得分:4)
想法是用户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));
}
答案 1 :(得分:3)
我已经设法完成了我的答案。我不确定这是否是最好的方式,希望其他人也可以评论。
对于设置:
<ion-view title="Page1" id="page18">
<ion-content padding="true" class="has-header">
<button id="page4-button4" class="button button-positive button-block" ng-click="scanBarcode()">Scan</button>
<button id="page4-button5" class="button button-positive button-block">Display Scanned Data</button>
然后拆掉
@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();
}
});
}
我的服务API
@After
public void tearDown() throws Exception {
RxJavaHooks.reset();
RxAndroidPlugins.getInstance().reset();
}
嘲笑
@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;
我举了一个成功案例的例子,作为一个例子。但是,因为一切似乎都可行。我只是想知道是否有更好的方法,或者我做了什么错误?
非常感谢提前