如何在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的内部缓存。
- 测试类中创建并绑定缓存实例
@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) } }
- 编写测试替换模块
注意要替换掉你项目中原本提供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 } }
方法二:自定义测试组件暴露缓存实例
如果第一种方法不适用,可以自定义测试组件,主动暴露缓存实例供测试类获取:
- 定义测试组件接口
@TestSingletonComponent interface TestSingletonComponent : SingletonComponent { // 暴露缓存实例,让测试类可以直接获取 fun getThreadSafeMemoryCache(): ThreadSafeMemoryCache<List<MusicFile>> // 自定义Builder,支持绑定缓存实例 @Component.Builder interface Builder : SingletonComponent.Builder { fun setThreadSafeMemoryCache(cache: ThreadSafeMemoryCache<List<MusicFile>>): Builder } }
- 测试类中初始化组件并获取缓存
@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
相关产品推荐
相关产品推荐

