Hilt测试报错:@TestInstallIn#replaces()仅支持@InstallIn标注模块
Hilt测试模块替换依赖报错解决
错误场景
为Instrumentation测试用TestCase模块提供Mock对象时,触发Hilt错误,错误信息如下:
[Hilt]
public final class TestModules {
^
@TestInstallIn#replaces() can only contain @InstallIn modules, but found: [com.davidozersky.posterpal.repo.UserRepository]
项目相关代码
主源码集的AppModule(提供真实依赖)
@Module @InstallIn(SingletonComponent::class) object AppModule { @Provides @Singleton fun provideJobApi(): JobsRetroInterface { val interceptor: Interceptor = object : Interceptor { @Throws(IOException::class) override fun intercept(chain: Interceptor.Chain): Response { var request: Request = chain.request() val builder: Request.Builder = request.newBuilder().addHeader("Cache-Control", "no-cache") request = builder.build() return chain.proceed(request) } } val client = OkHttpClient.Builder() .addInterceptor(interceptor) .cache(null) .build() val retrofit = Retrofit.Builder() .baseUrl(Constants.BASE_URL) .addConverterFactory(MoshiConverterFactory.create()) .client(client) .build() return retrofit.create(JobsRetroInterface::class.java) } @Provides @Singleton fun provideJobDao(database : JobDatabase) : JobDao { return database.jobDao } @Provides @Singleton fun provideDatabase(@ApplicationContext application : Context) : JobDatabase { return Room.databaseBuilder(application, JobDatabase::class.java, JobDatabase.Companion.NAME).fallbackToDestructiveMigration().build() } @Provides @Singleton fun provideFireBaseAuth(): FirebaseAuth { return FirebaseAuth.getInstance() } @Provides @Singleton fun provideUserRepository() : UserRepository { return UserRepositoryImpl(FirebaseAuth.getInstance()) } }
测试用的TestModules(尝试替换为Mock对象)
package com.davidozersky.posterpal import android.app.Activity import com.davidozersky.posterpal.api.JobsRetroInterface import com.davidozersky.posterpal.database.JobDao import com.davidozersky.posterpal.database.entities.* import com.davidozersky.posterpal.database.relations.OwnerWithJobs import com.davidozersky.posterpal.database.relations.jobworkerposter.JobPW import com.davidozersky.posterpal.database.relations.posterworker.WorkerWithPosters import com.davidozersky.posterpal.repo.JobsRepository import com.davidozersky.posterpal.repo.UserRepository import com.davidozersky.posterpal.repo.UserRepositoryImpl import com.davidozersky.posterpal.ui.fragments.SignUpFragment import dagger.Binds import dagger.Module import dagger.Provides import dagger.hilt.android.components.FragmentComponent import dagger.hilt.components.SingletonComponent import dagger.hilt.testing.TestInstallIn import io.mockk.mockk import kotlinx.coroutines.flow.Flow import retrofit2.Response import javax.inject.Inject import javax.inject.Singleton @Module @TestInstallIn( components = [SingletonComponent::class], replaces = [UserRepository::class] ) object TestModules { @Provides @Singleton fun provideUserService() : UserRepository { return FakeUserRepo() } class FakeJobsRepo(override var appliedAndAcceptedJobs: List<JobPW>) : JobsRepository { override fun getDao(): JobDao { TODO("Not yet implemented") } override fun getApi(): JobsRetroInterface { TODO("Not yet implemented") } override suspend fun getAllJobs(): List<JobPW> { TODO("Not yet implemented") } override fun getAllJobsFlow(): Flow<List<JobPW>> { TODO("Not yet implemented") } override suspend fun getJobById(id: Int): JobPW? { TODO("Not yet implemented") } override suspend fun insertJob(job: Job) { TODO("Not yet implemented") } override suspend fun getOwner(id: Int): OwnerWithJobs { TODO("Not yet implemented") } override suspend fun insertOwner(owner: Owner): Long { TODO("Not yet implemented") } override suspend fun getPoster(id: Int): Poster? { TODO("Not yet implemented") } override suspend fun insertPoster(poster: Poster) { TODO("Not yet implemented") } override suspend fun getWorkerWithPoster(worker_id: Long): Flow<WorkerWithPosters> { TODO("Not yet implemented") } override suspend fun getJobsByAPI(): Response<List<JobPW>>? { TODO("Not yet implemented") } override suspend fun addWorkersToJob() { TODO("Not yet implemented") } override suspend fun addPostersToJob() { TODO("Not yet implemented") } override suspend fun saveJobToDatabase(job: JobPW): Long { TODO("Not yet implemented") } override suspend fun addWorkerToPoster(poster_id: Long, worker: Worker) { TODO("Not yet implemented") } } class FakeUserRepo : UserRepository { suspend fun getUsers() = mockk<List<User>>() override fun checkForUser() { TODO("Not yet implemented") } override fun signUpWithEmailAndPassword( email: String?, password: String?, activity: Activity, signUpListener: SignUpFragment.SignUpListener ) { TODO("Not yet implemented") } } }
错误原因
报错核心是**@TestInstallIn的replaces参数只能接收带有@InstallIn注解的模块类**,而你传入的UserRepository是业务接口,不是Hilt模块,不符合要求。
解决方案
方案1:拆分模块(推荐)
将UserRepository的提供逻辑从AppModule中拆分出来,单独创建UserModule:
// 主源码集新建UserModule.kt @Module @InstallIn(SingletonComponent::class) object UserModule { @Provides @Singleton fun provideUserRepository() : UserRepository { return UserRepositoryImpl(FirebaseAuth.getInstance()) } }
修改AppModule,删除原来的provideUserRepository方法。
最后修改测试模块的@TestInstallIn注解,替换目标为UserModule:
@Module @TestInstallIn( components = [SingletonComponent::class], replaces = [UserModule::class] // 替换为拆分后的模块 ) object TestModules { // ... 原有代码不变 }
方案2:卸载原模块并重新提供所有依赖
如果不想拆分模块,可以在测试类上使用@UninstallModules(AppModule::class)卸载整个AppModule,然后在TestModules中提供所有需要的依赖(包括原AppModule中的其他依赖,可用Mock替代):
- 测试类添加注解:
@HiltAndroidTest @UninstallModules(AppModule::class) class YourInstrumentationTest { // ... 测试代码 }
- 修改TestModules,补充原AppModule中的其他依赖提供(示例用Mock替代):
@Module @TestInstallIn(components = [SingletonComponent::class]) object TestModules { @Provides @Singleton fun provideUserService() : UserRepository { return FakeUserRepo() } // 补充原AppModule中的其他依赖,这里用mockk示例 @Provides @Singleton fun provideJobApi(): JobsRetroInterface = mockk() @Provides @Singleton fun provideJobDao(database : JobDatabase) : JobDao = mockk() @Provides @Singleton fun provideDatabase(@ApplicationContext application : Context) : JobDatabase = mockk() @Provides @Singleton fun provideFireBaseAuth(): FirebaseAuth = mockk() // ... 原有Fake类代码不变 }
这样就能解决Hilt报错,成功替换为Mock对象进行测试。
内容的提问来源于stack exchange,提问作者user1743524
相关产品推荐
相关产品推荐

