I am working on instrument test in android studio. My app is Dynamic Feature Module use both dagger and hilt to provide dependency. I am following this doc https://developer.android.com/training/dependency-injection/hilt-testing#replace-binding As it said I need to Replace a binding but when on build I got error
com.xxxxxxx.CartRepository cannot be provided without an @Provides-annotated method.
public abstract static class SingletonC implements CheckoutFragmentTest_GeneratedInjector,
This is my dagger hilt version
"com.google.dagger:hilt-android:2.38.1"
"com.google.dagger:hilt-compiler:2.38.1"
"com.google.dagger:hilt-android-testing:2.38.1"
"com.google.dagger:hilt-android-compiler:2.38.1"
"androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha02"
"androidx.hilt:hilt-compiler:1.0.0-alpha02"
"androidx.hilt:hilt-common:1.0.0-alpha02"
Here is how I provide dependency in production code is feature module
Inject.kt file in feature cart module
//Inject.kt file in feature cart module
internal fun inject(fragment: CheckoutFragment) {
DaggerCartComponent
.factory()
.create(
fragment,
coreComponent(fragment),
appComponent(fragment)
)
.inject(fragment)
}
private fun appComponent(fragment: Fragment): AppComponent =
EntryPointAccessors.fromActivity(
fragment.requireActivity(),
AppComponent::class.java
)
private fun coreComponent(fragment: Fragment): CoreComponent =
EntryPointAccessors.fromApplication(
fragment.requireActivity().applicationContext,
CoreComponent::class.java
)
feature cart module component
//feature cart module component
@Component(
dependencies = [CoreComponent::class, AppComponent::class],
modules = [
CartViewModelModule::class,
CartDataModule::class
]
)
interface CartComponent {
fun inject(fragment: CartFragment)
fun inject(fragment: CheckoutFragment)
fun inject(fragment: CartNavigationFragment)
fun inject(fragment: PickupInformationFragment)
@Component.Factory
interface Factory {
fun create(
@BindsInstance fragment: Fragment,
coreComponent: CoreComponent,
appComponent: AppComponent
): CartComponent
}
}
CartDataModule
@[Module InstallIn(FragmentComponent::class)]
abstract class CartDataModule {
@Binds
abstract fun provideCartRepository(cartRepositoryImpl: CartRepositoryImpl) : CartRepository
@Binds
abstract fun provideCheckOutRepository(checkoutRepositoryImpl: CheckoutRepositoryImpl) : CheckoutRepository
@Binds
abstract fun provideMainRepository(mainRepositoryImpl: MainRepositoryImpl): MainRepository
}
I use this as a factory to create my viewmodel that hold my repository
class DFMSavedStateViewModelFactory(
owner: SavedStateRegistryOwner,
defaultArgs: Bundle?,
private val delegateFactory: SavedStateViewModelFactory,
private val viewModelFactories: @JvmSuppressWildcards Map<String, Provider<ViewModelAssistedFactory<out ViewModel>>>,
) : AbstractSavedStateViewModelFactory(owner, defaultArgs) {
@SuppressLint("RestrictedApi")
override fun <T : ViewModel?> create(
key: String,
modelClass: Class<T>,
handle: SavedStateHandle
): T {
val factoryProvider = viewModelFactories[modelClass.name]
?: return delegateFactory.create("$KEY_PREFIX:$key", modelClass)
@Suppress("UNCHECKED_CAST")
return factoryProvider.get().create(handle) as T
}
companion object {
private const val KEY_PREFIX = "androidx.hilt.lifecycle.HiltViewModelFactory"
}
}
And Here is my code under androidTest folder
CheckoutFragmentTest.kt
@ExperimentalCoroutinesApi
@UninstallModules(
CartDataModule::class,
)
@HiltAndroidTest
class CheckoutFragmentTest {
@Module
@InstallIn(FragmentComponent::class)
abstract class TestModule1 {
@Binds
@Singleton
abstract fun provideCartRepository(cartRepositoryImpl: FakeCartRepositoryImpl): CartRepository
@Binds
@Singleton
abstract fun provideCheckOutRepository(checkoutRepositoryImpl: FakeCheckoutRepositoryImpl): CheckoutRepository
}
@get:Rule
var hiltRule = HiltAndroidRule(this)
@get:Rule
var activityScenarioRule = activityScenarioRule<MainActivity>()
@Before
fun init() {
hiltRule.inject()
}
@Test
fun testCheckoutUICase1() {
launchFragmentInHiltContainer<CheckoutFragment> {
}
}
}
And this FakeCartDataModule that assume to replace CartDataModule in production code
@Module
@TestInstallIn(
components = [FragmentComponent::class],
replaces = [CartDataModule::class]
)
abstract class FakeCartDataModule {
@Binds
abstract fun provideCartRepository(cartRepositoryImpl: FakeCartRepositoryImpl): CartRepository
@Binds
abstract fun provideCheckOutRepository(checkoutRepositoryImpl: FakeCheckoutRepositoryImpl): CheckoutRepository
}
Does anyone know why I cannot provide FakeCartDataModule to run my androidTest? Despite of I already following along the doc