0

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

0 Answers0