如何为Retrofit+RxJava实现的网络数据加载映射代码编写单元测试?
如何为结合Retrofit+RxJava的数据加载代码编写单元测试?
先来看下你提供的核心代码片段:
@Override public void onLoadData() { view.showProgressBar(); service.fetchCVRF(createRequestBody()) .map(this::mapToViewModels) .subscribeOn(schedulerProvider.io()) .observeOn(schedulerProvider.mainThread()) .subscribe(viewModels -> { if (view != null) { view.hideProgressBar(); view.showData(viewModels); } }, throwable -> { ... }); }
要测试这段代码,我们需要覆盖正常流程、异常流程,以及核心的映射逻辑,同时还要处理RxJava的线程调度问题(避免异步测试的时序混乱)。下面我一步步给你拆解实现方案:
准备测试工具
我们会用到这些工具:
- Mockito:用来Mock View、Retrofit Service、SchedulerProvider这些外部依赖,让测试只聚焦于Presenter的逻辑
- RxJava TestScheduler:用来同步控制RxJava的线程调度,不用等待异步任务完成
- JUnit + AssertJ:做断言和测试用例管理
步骤1:初始化测试环境
首先在测试类中创建并初始化所有Mock对象和TestScheduler:
@Mock private YourView view; // 替换成你的View接口类型 @Mock private YourCVRFService service; // 替换成你的Retrofit Service接口 @Mock private SchedulerProvider schedulerProvider; private TestScheduler testScheduler; private YourPresenter presenter; // 替换成包含onLoadData方法的Presenter类 @Before public void setUp() { MockitoAnnotations.openMocks(this); testScheduler = new TestScheduler(); // 让SchedulerProvider返回TestScheduler,这样所有Rx任务都在同一个线程同步执行 when(schedulerProvider.io()).thenReturn(testScheduler); when(schedulerProvider.mainThread()).thenReturn(testScheduler); // 初始化Presenter,传入Mock的依赖 presenter = new YourPresenter(service, view, schedulerProvider); }
步骤2:测试数据加载成功的场景
这个测试要验证:
- 调用
onLoadData后是否显示进度条 - Retrofit服务是否被正确调用
- 数据映射是否正常
- 成功后是否隐藏进度条并展示数据
@Test public void onLoadData_whenSuccess_shouldShowDataCorrectly() { // 1. 准备测试数据 CVRFResponse mockApiResponse = new CVRFResponse(); // 替换成你的接口返回实体 List<YourViewModel> expectedViewModels = Arrays.asList(new YourViewModel("测试数据1"), new YourViewModel("测试数据2")); // 2. Mock Service的返回结果 when(service.fetchCVRF(any(RequestBody.class))) .thenReturn(Observable.just(mockApiResponse)); // 3. 如果mapToViewModels是Presenter的方法,我们可以Mock它的返回(或者用真实逻辑,看你是集成测试还是单元测试) doReturn(expectedViewModels).when(presenter).mapToViewModels(mockApiResponse); // 4. 触发目标方法 presenter.onLoadData(); // 5. 手动触发TestScheduler执行所有Rx任务(因为我们把线程都换成了TestScheduler,需要手动触发) testScheduler.triggerActions(); // 6. 验证所有预期行为 verify(view).showProgressBar(); // 一开始显示进度条 verify(service).fetchCVRF(any(RequestBody.class)); // 调用了Service的fetch方法 verify(view).hideProgressBar(); // 成功后隐藏进度条 verify(view).showData(expectedViewModels); // 调用View展示正确的ViewModel列表 // 如果需要验证调用顺序,可以用InOrder InOrder inOrder = inOrder(view); inOrder.verify(view).showProgressBar(); inOrder.verify(view).hideProgressBar(); inOrder.verify(view).showData(expectedViewModels); }
步骤3:测试数据加载失败的场景
这个测试要验证异常情况下的处理逻辑:
@Test public void onLoadData_whenError_shouldHandleErrorProperly() { // 1. 准备测试异常 Throwable mockNetworkError = new IOException("网络连接失败"); // 2. Mock Service返回错误Observable when(service.fetchCVRF(any(RequestBody.class))) .thenReturn(Observable.error(mockNetworkError)); // 3. 触发目标方法 presenter.onLoadData(); // 4. 触发Rx任务执行 testScheduler.triggerActions(); // 5. 验证行为 verify(view).showProgressBar(); verify(view).hideProgressBar(); // 即使出错也要隐藏进度条,避免界面卡住 verify(view).showError("加载失败,请稍后重试"); // 替换成你View中对应的错误提示方法 }
步骤4:单独测试映射逻辑(可选但推荐)
如果mapToViewModels方法逻辑比较复杂,建议单独写测试用例,确保数据映射的正确性:
@Test public void mapToViewModels_shouldConvertApiResponseToViewModelCorrectly() { // 1. 构造完整的API返回数据 CVRFResponse apiResponse = new CVRFResponse(); apiResponse.setItems(Arrays.asList( new CVRFItem("标题1", "内容1"), new CVRFItem("标题2", "内容2") )); // 2. 调用映射方法 List<YourViewModel> result = presenter.mapToViewModels(apiResponse); // 3. 验证映射结果 assertThat(result).hasSize(2); assertThat(result.get(0).getTitle()).isEqualTo("标题1"); assertThat(result.get(0).getContent()).isEqualTo("内容1"); assertThat(result.get(1).getTitle()).isEqualTo("标题2"); }
关键注意事项
- 线程调度控制:一定要用TestScheduler替换掉真实的IO和Main线程,否则异步任务会导致测试在回调执行前就结束,断言失败。
- Mock所有外部依赖:View、Service、SchedulerProvider都要Mock,这样测试不会受外部服务(比如真实网络请求)的影响,保证测试的稳定性。
- 空指针处理:因为我们Mock了View,所以不用考虑
view != null的判断,Mockito会处理空指针问题。 - 测试边界场景:比如API返回空列表的情况,也要单独写测试验证View的表现是否符合预期。
内容的提问来源于stack exchange,提问作者Mes
相关产品推荐
相关产品推荐

