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

如何在Hilt测试中获取注入的ThreadSafeMemoryCache进行验证?

问题描述

我想在测试时通过Hilt给被测的MusicRepository注入一个ThreadSafeMemoryCache,不用给缓存加getter就能在测试里检查它的状态。手动DI很容易实现——先构造好缓存实例再注入给Repository,但用Hilt不知道怎么操作。

我试了下面的写法,但没法同时在测试模块和测试类里访问同一个threadSafeMemoryCache实例:

@Inject
lateinit var threadSafeMemoryCache: ThreadSafeMemoryCache<List<MusicFile>>

@Module
@TestInstallIn(
    components = [SingletonComponent::class],
    replaces = [ThreadSafeMemoryCache::class]
)
abstract class ThreadSafeMemoryCacheModule {

    @Singleton
    @Provides
    fun provideThreadSafeMemoryCache(): ThreadSafeMemoryCache<List<MusicFile>>{
        return threadSafeMemoryCache
    }

}

我的目标是拿到注入到Repository里的缓存实例,验证它是否被正确使用。

相关类代码:
MusicRepository:

@Singleton
class MusicRepository @Inject constructor() {

    @Inject
    internal lateinit var musicCache: ThreadSafeMemoryCache<List<MusicFile>>
    // ... 其他代码
}

测试类里注入Repository:

@Inject
lateinit var musicRepository: MusicRepository
解决方案

方法一:测试类预创建实例,通过模块绑定共享

核心逻辑是在测试类里先创建好缓存实例,让Hilt的测试模块提供这个实例,这样测试类和MusicRepository拿到的是同一个对象,不用暴露Repository的内部缓存。

  1. 测试类中创建并绑定缓存实例
@HiltAndroidTest
class MusicRepositoryTest {

    // 提前创建测试用的缓存实例
    private val testCache = ThreadSafeMemoryCache<List<MusicFile>>()

    @Inject
    lateinit var musicRepository: MusicRepository

    @Before
    fun setUp() {
        // 初始化Hilt并注入依赖
        val rule = HiltAndroidRule(this)
        // 用TestComponentBuilder绑定我们的测试缓存
        rule.componentBuilder()
            .addBinding(ThreadSafeMemoryCache::class.java)
            .toInstance(testCache)
        rule.inject()
    }

    // 测试时直接用testCache验证状态
    @Test
    fun `check cache is updated after loading music`() {
        // 执行Repository的业务操作
        musicRepository.loadMusicList()
        // 直接检查testCache的内容
        assertThat(testCache.get(CACHE_KEY)).isEqualTo(expectedMusicList)
    }
}
  1. 编写测试替换模块
    注意要替换掉你项目中原本提供ThreadSafeMemoryCache的模块,避免Hilt注入默认实例:
@Module
@TestInstallIn(
    components = [SingletonComponent::class],
    replaces = [OriginalCacheModule::class] // 替换你的原始缓存模块
)
class TestCacheModule {

    @Singleton
    @Provides
    fun provideThreadSafeMemoryCache(
        // 这里注入的就是我们在测试类里绑定的testCache实例
        testCache: ThreadSafeMemoryCache<List<MusicFile>>
    ): ThreadSafeMemoryCache<List<MusicFile>> {
        return testCache
    }
}

方法二:自定义测试组件暴露缓存实例

如果第一种方法不适用,可以自定义测试组件,主动暴露缓存实例供测试类获取:

  1. 定义测试组件接口
@TestSingletonComponent
interface TestSingletonComponent : SingletonComponent {
    // 暴露缓存实例,让测试类可以直接获取
    fun getThreadSafeMemoryCache(): ThreadSafeMemoryCache<List<MusicFile>>

    // 自定义Builder,支持绑定缓存实例
    @Component.Builder
    interface Builder : SingletonComponent.Builder {
        fun setThreadSafeMemoryCache(cache: ThreadSafeMemoryCache<List<MusicFile>>): Builder
    }
}
  1. 测试类中初始化组件并获取缓存
@HiltAndroidTest
class MusicRepositoryTest {

    private lateinit var testCache: ThreadSafeMemoryCache<List<MusicFile>>
    @Inject
    lateinit var musicRepository: MusicRepository

    @Before
    fun setUp() {
        testCache = ThreadSafeMemoryCache()
        // 创建测试组件并绑定缓存实例
        val component = DaggerTestSingletonComponent.builder()
            .setThreadSafeMemoryCache(testCache)
            .build()
        component.inject(this)
    }

    @Test
    fun `verify cache is used after data fetch`() {
        musicRepository.fetchAndCacheMusic()
        // 直接用testCache验证缓存状态
        assertThat(testCache.contains(CACHE_KEY)).isTrue()
    }
}

注意事项

  • 必须确保测试模块替换了原始的缓存提供模块,否则Hilt会注入默认的缓存实例,导致测试类和Repository拿到的不是同一个对象
  • 不用修改MusicRepository的代码,保持musicCache的internal可见性即可,无需添加public getter
  • 两种方法的核心都是让测试类和Repository共享同一个缓存实例,这样测试中的状态检查才有效

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 02:05:34