You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 04:28:26