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

在Espresso测试中取消或覆写Activity内的API调用

嘿,这个问题我太熟了!在UI测试里碰到这种不可控的API请求确实头疼,尤其是还会触发ViewStub替换界面,直接打乱测试流程。下面给你几个靠谱的方案,帮你把API请求的结果牢牢握在手里:

方案一:依赖注入+测试替身(Test Double)—— 最推荐的长期方案

这是解耦业务逻辑和测试的标准做法,核心是把API调用抽象成接口,然后在测试环境用假实现替换真实的API客户端。

步骤:

  1. 抽象API服务接口
    先把你的API调用逻辑封装成一个接口,比如:
    interface ApiService {
        suspend fun fetchRequiredData(): Result<YourDataModel>
    }
    
  2. 实现真实和测试用的版本
    • 真实环境用RealApiService,里面写实际的Retrofit/OkHttp调用逻辑
    • 测试环境用FakeApiService,直接返回你预设的成功/失败结果:
      class FakeApiService(private val presetResult: Result<YourDataModel>) : ApiService {
          override suspend fun fetchRequiredData(): Result<YourDataModel> {
              return presetResult
          }
      }
      
  3. 在Activity中注入接口实例
    用构造函数注入或者依赖注入框架(比如Hilt、Dagger)把ApiService注入到Activity里,而不是在Activity内部直接创建真实实例:
    class YourActivity : AppCompatActivity() {
        private val apiService: ApiService by inject() // 或者构造函数传参
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            lifecycleScope.launch {
                val result = apiService.fetchRequiredData()
                if (result.isFailure) {
                    errorViewStub.inflate()
                } else {
                    // 渲染正常业务界面
                }
            }
        }
    }
    
  4. 测试时替换为Fake实现
    比如用Hilt的话,可以通过@TestInstallIn注解在测试模块中替换ApiService的绑定;如果是手动注入,就在启动Activity前把FakeApiService传进去,直接控制返回结果,完全避免真实网络请求。

方案二:用Mockito Mock API客户端—— 快速适配现有代码

如果不想大改现有代码结构,可以直接用Mockito等Mock框架,把Activity里的API客户端实例替换成Mock对象,预设它的返回值。

举个测试代码的例子:

@RunWith(AndroidJUnit4::class)
class YourActivityTest {
    @Mock
    private lateinit var mockApiService: ApiService

    @Before
    fun setup() {
        MockitoAnnotations.openMocks(this)
        // 启动Activity前,替换掉里面的apiService实例(这里用反射,也可以结合依赖注入)
        val activityScenario = launchActivity<YourActivity>()
        activityScenario.onActivity { activity ->
            val field = YourActivity::class.java.getDeclaredField("apiService")
            field.isAccessible = true
            field.set(activity, mockApiService)
        }
    }

    @Test
    fun testApiSuccess_DoesNotShowErrorStub() {
        // 预设API调用返回成功
        Mockito.`when`(mockApiService.fetchRequiredData())
            .thenReturn(Result.success(YourDataModel()))
        
        // 断言错误Stub没有被加载
        onView(withId(R.id.error_view_stub)).check(doesNotExist())
    }

    @Test
    fun testApiFailure_ShowsErrorStub() {
        // 预设API调用返回失败
        Mockito.`when`(mockApiService.fetchRequiredData())
            .thenReturn(Result.failure(ApiException()))
        
        // 断言错误Stub已被加载
        onView(withId(R.id.error_layout)).check(matches(isDisplayed()))
    }
}

⚠️ 注意:如果API调用是在onCreate里立刻执行的,要确保在Activity的onCreate触发前就完成Mock对象的替换,不然可能会用到真实的API客户端。

方案三:用OkHttp拦截器拦截网络请求—— 无侵入式拦截

如果完全不想修改业务代码,可以在测试环境给OkHttp加一个拦截器,直接拦截所有API请求,返回你预设的响应。

实现拦截器:

class MockNetworkInterceptor(private val mockResponse: Response) : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        // 直接返回预设的响应,不走真实网络
        return mockResponse.newBuilder()
            .request(chain.request())
            .build()
    }
}

测试时配置Retrofit:

// 构建一个成功的Mock响应
val successResponse = Response.Builder()
    .code(200)
    .body(ResponseBody.create(MediaType.parse("application/json"), "{\"key\":\"value\"}"))
    .build()

// 配置带拦截器的OkHttpClient
val mockOkHttpClient = OkHttpClient.Builder()
    .addInterceptor(MockNetworkInterceptor(successResponse))
    .build()

// 用这个客户端创建Retrofit实例,替换掉真实环境的Retrofit
val mockRetrofit = Retrofit.Builder()
    .baseUrl("https://your-real-base-url.com/")
    .client(mockOkHttpClient)
    .build()

这样所有的API请求都会被拦截,返回你预设的结果,不管是成功还是失败。

额外注意点

  • 不管用哪种方案,都要确保测试环境不会发起真实网络请求,避免依赖外部环境,保证测试的可重复性。
  • 如果用协程执行API调用,测试时要注意等待协程完成:可以用EspressoIdlingResource标记网络请求状态,或者用Jetpack的lifecycle-testing库来控制协程的执行时机,避免Espresso在协程完成前就进行断言。

内容的提问来源于stack exchange,提问作者Orbit

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 06:28:44