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

如何通过Hilt配置Retrofit测试URL以适配MockWebServer

如何在使用Hilt的Android项目中配合MockWebServer动态修改Retrofit的BaseUrl进行测试?

首先,你现有代码里把baseUrl作为object的可变属性,这种方式在测试场景下会有全局状态污染的问题——不同测试用例之间可能互相干扰,也不符合依赖注入的设计原则。我们可以通过Hilt的测试替换机制优雅解决这个问题,下面分两种方案说明:

方案一:重构原模块+测试模块替换

第一步:优化生产环境的RetrofitModule

先把硬编码的baseUrl抽成单独的依赖提供方法,让Hilt来管理它,这样测试时就能轻松替换:

@InstallIn(ApplicationComponent::class)
object RetrofitModule {

    // 将BaseUrl改为通过@Provides提供
    @Singleton
    @Provides
    fun provideBaseUrl(): String {
        return "https://my.fancy.api"
    }

    @Singleton
    @Provides
    fun providesRetrofitClient(
        baseUrl: String, // 注入BaseUrl依赖
        okHttpClient: OkHttpClient
    ): Retrofit {
        return Retrofit.Builder()
            .baseUrl(baseUrl)
            .addConverterFactory(GsonConverterFactory.create())
            .client(okHttpClient)
            .build()
    }

    // OkHttpClient和Service的提供方法保持不变
    @Singleton
    @Provides
    fun providesOkHttpClient(): OkHttpClient {
        val okHttpClientBuilder = OkHttpClient.Builder()
        val loggingInterceptor = HttpLoggingInterceptor().apply {
            level = HttpLoggingInterceptor.Level.BODY
        }
        okHttpClientBuilder.addInterceptor(loggingInterceptor)
        return okHttpClientBuilder.build()
    }

    @Singleton
    @Provides
    fun providesJokeGeneratorService(retrofit: Retrofit): FancyApiService {
        return retrofit.create(FancyApiService::class.java)
    }
}

第二步:创建测试专用的Retrofit模块

用@TestInstallIn注解创建测试模块,替换生产环境的RetrofitModule,并提供MockWebServer的BaseUrl:

@Module
@TestInstallIn(
    components = [ApplicationComponent::class],
    replaces = [RetrofitModule::class]
)
object TestRetrofitModule {

    // 注入MockWebServer,返回它的BaseUrl
    @Singleton
    @Provides
    fun provideBaseUrl(mockWebServer: MockWebServer): String {
        return mockWebServer.url("/").toString()
    }

    // 可复用生产环境的OkHttpClient配置,也可根据测试需求调整
    @Singleton
    @Provides
    fun providesOkHttpClient(): OkHttpClient {
        val okHttpClientBuilder = OkHttpClient.Builder()
        val loggingInterceptor = HttpLoggingInterceptor().apply {
            level = HttpLoggingInterceptor.Level.BODY
        }
        okHttpClientBuilder.addInterceptor(loggingInterceptor)
        return okHttpClientBuilder.build()
    }

    @Singleton
    @Provides
    fun providesJokeGeneratorService(retrofit: Retrofit): FancyApiService {
        return retrofit.create(FancyApiService::class.java)
    }
}

第三步:编写测试类

在测试类中初始化Hilt和MockWebServer,注入需要测试的API服务:

@HiltAndroidTest
class FancyApiRepositoryTest {

    @get:Rule
    val hiltRule = HiltAndroidRule(this)

    @Inject
    lateinit var mockWebServer: MockWebServer

    @Inject
    lateinit var fancyApiService: FancyApiService

    @Before
    fun setUp() {
        hiltRule.inject()
        // 启动MockWebServer
        mockWebServer.start()
    }

    @After
    fun tearDown() {
        // 测试结束后关闭MockWebServer
        mockWebServer.shutdown()
    }

    @Test
    fun `test fetch fancy data`() = runTest {
        // 配置MockWebServer的模拟响应
        mockWebServer.enqueue(MockResponse()
            .setBody("""{"data": "test content"}""")
            .setResponseCode(200))

        // 调用API服务
        val response = fancyApiService.fetchData()

        // 断言结果符合预期
        assertEquals("test content", response.data)
    }
}

方案二:用@BindValue快速替换BaseUrl(更轻量)

如果你不想编写完整的测试模块,可以直接在测试类中用@BindValue注解提供测试用的BaseUrl,前提是你已经完成了方案一的第一步重构:

@HiltAndroidTest
class FancyApiRepositoryTest {

    @get:Rule
    val hiltRule = HiltAndroidRule(this)

    @Inject
    lateinit var mockWebServer: MockWebServer

    // 绑定测试用的BaseUrl,自动替换生产环境的provideBaseUrl
    @BindValue
    val testBaseUrl: String by lazy { mockWebServer.url("/").toString() }

    @Inject
    lateinit var fancyApiService: FancyApiService

    @Before
    fun setUp() {
        hiltRule.inject()
        mockWebServer.start()
    }

    @After
    fun tearDown() {
        mockWebServer.shutdown()
    }

    // 测试方法同上...
}

这种方式适合只需要替换单个依赖的场景,代码更简洁。

为什么不推荐直接修改object的var?

直接修改RetrofitModule.baseUrl会导致全局状态被修改,比如A测试用例改了BaseUrl后,B测试用例如果没还原,就会用错误的地址测试,引发不可预期的问题。而用Hilt的测试替换机制,每个测试用例都是隔离的,不会互相干扰。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 14:18:11