Search code examples
androiddaggerdagger-hilt

Duplicate bindings with TestInstallIn for instrumented test


I'm using Hilt to inject some dependencies like so:

@Module
@InstallIn(SingletonComponent::class)
object SomethingModule {

    @Provides
    fun provideSomething(@ApplicationContext context: Context): Something {
        //return RealSomething
    }
}

I want to replace this binding in my tests and following this guide I added this fake module to the same folder as my instrumented test:

@Module
@TestInstallIn(
    components = [SingletonComponent::class],
    replaces = [SomethingModule::class]
)
object FakeSomethingModuleModule {

    @Provides
    fun provideSomethingModule(): Something {
        return FakeSomething()
    }
}

When I try to run my instrumented test the build fails with a duplicate binding error:

error: [Dagger/DuplicateBindings] com.example.Something is bound multiple times:
  public abstract static class SingletonC implements HiltWrapper_ActivityRetainedComponentManager_ActivityRetainedComponentBuilderEntryPoint,

Commenting out the Fake Module fixes the issue. Anyone see what I'm missing here?

EDIT: Just tried with @UninstallModules and ran into the same build error:

@UninstallModules(SomethingModule::class)
@HiltAndroidTest
@RunWith(AndroidJUnit4::class)
class MyInstrumentedTest {

    @Module
    @InstallIn(SingletonComponent::class)
    object FakeSomethingModule {

        @Provides
        fun provideSomthing(): Something = FakeSomething()
    }

Solution

  • I tried lots of different things in order to finally get to a solution:

    1. Make sure the fake implementations have @Inject constructor()

    2. In the test @Module use @Binds and an abstract class instead of @Provides

    3. You don't need a component that combines all your modules, this code is not necessary:

    @Module(
        includes = [ <modules> ]
    
    1. It wasn't apparent in this sample but in my actual code the module I was trying to test was included as a binding to another class using @Module(includes = SomethingBinding). I created a module just for the object I wanted to mock in my instrumented tests