如何让Hilt注入实现类而非接口以完成ViewModel测试?
问题场景
我在编写测试代码时遇到如下问题:
拥有仓库类和接口:
class ItemsRepository @Inject constructor( private val api: MercadoLibreApi ): IItemsRepository {
interface IItemsRepository { suspend fun getSearch(search: String): Resource<ResponseML> }
ViewModel类:
@HiltViewModel class MainViewModel @Inject constructor( val repo: ItemsRepository ) : ViewModel() {
还有Dagger/Hilt注入的Retrofit实例:
@Provides @Singleton fun providesMercadoLibreApi(retrofit: Retrofit): MercadoLibreApi { return retrofit.create(MercadoLibreApi::class.java) }
在MainViewModelTest中想使用MockupItemsRepository:
private lateinit var itemsRepository : MockupItemsRepository
该类定义:
class MockupItemsRepository: IItemsRepository { .. }
在@Before方法初始化ViewModel:
@Before fun setup() { viewModel = MainViewModel(itemsRepository) }
此时因itemsRepository是MockupItemsRepository类型报错,于是将ViewModel构造参数改为接口:
val repo: IItemsRepository // 使用接口
测试错误消失,但编译时Hilt提示:
IItemsRepository cannot be provided without an @Provides-annotated method
请问如何让Hilt注入实现类而非接口?
解决方案
要解决这个矛盾,需要给Hilt配置接口与实现类的绑定关系,让Hilt知道当需要IItemsRepository时注入ItemsRepository实例,同时保持ViewModel使用接口作为构造参数,方便测试时传入Mock类。
步骤1:创建Hilt绑定Module
新建一个抽象Module类,用@Binds注解绑定接口和实现类(这种方式比@Provides更高效,无需额外实例化逻辑):
@Module @InstallIn(SingletonComponent::class) // 可按需调整作用域,比如ViewModelComponent abstract class RepositoryModule { @Binds @Singleton // 与ItemsRepository的依赖作用域保持一致 abstract fun bindItemsRepository( itemsRepository: ItemsRepository ): IItemsRepository }
@Binds要求方法为抽象方法,Module类也必须是抽象类@InstallIn指定绑定的作用域,这里用SingletonComponent适配MercadoLibreApi的单例生命周期
步骤2:保持ViewModel用接口作为构造参数
保留已修改的ViewModel构造参数:
@HiltViewModel class MainViewModel @Inject constructor( val repo: IItemsRepository ) : ViewModel() {
步骤3:测试时正常传入Mock类
现在MockupItemsRepository实现了IItemsRepository,可以直接传入ViewModel构造方法,类型完全匹配:
@Before fun setup() { itemsRepository = MockupItemsRepository() viewModel = MainViewModel(itemsRepository) }
原理说明
- Hilt通过
RepositoryModule的@Binds注解,明确了IItemsRepository的实现类是ItemsRepository,编译时自动生成注入逻辑 - ViewModel依赖接口,生产环境由Hilt注入真实实现,测试环境手动传入Mock类,同时满足依赖注入规范和测试灵活性
内容的提问来源于stack exchange,提问作者Nicolas400
相关产品推荐
相关产品推荐

