1

I'm trying to run an end-to-end test on an app that uses Dagger and Room, When I run the app Dagger isn't able to generate DaggerTestAppComponent.create().

I've been following the Dagger codelab to implement the Dagger into the tests.

Here's a link to the repository on Github. https://github.com/Shawn-Nichol/EndToEnd

Step 1. Custom Test Runner User a custom test runner to use a different application class.

class MyCustomTestRunner : AndroidJUnitRunner() {

    override fun newApplication(cl: ClassLoader?, name: String?, context: Context?): Application {
        return super.newApplication(cl, MyTestApplication::class.java.name, context)
    }
}

Step 2 add to app build gradle. The custom test runner is specified in the app build Gradle.

testInstrumentationRunner "com.example.endtoend.dagger.MyCustomTestRunner"

Step 3 load Modules TestAppComponent, Graph for AndroidTest scope set.


@Singleton
@Component(modules = [TestSaveDataStorageModule::class, RoomModule::class, DispatchersModule::class])
interface TestAppComponent : AppComponent {
    @Component.Factory
    interface Factory {
        fun create(@BindsInstance context: Context): AppComponent
    }
}

Step 4 Add dependencies In order to generate DaggerTestAppComponent The kapt needs to act on the androidTest source set.

dependencies {
    ...
    kaptAndroidTest "com.google.dagger:dagger-compiler:$dagger_version"
}

step 5 update application MyApplication

open class MyApplication : Application() {
    val appComponent: AppComponent by lazy {
        initializeComponent()
    }
    open fun initializeComponent(): AppComponent {
        return DaggerAppComponent.factory().create(applicationContext)
    }
}

Step 6 TestApplication

class TestApplication : MyApplication() {

    override fun initializeComponent(): AppComponent {
        return DaggerTestAppComponent.create()
    }
}

After completing these steps, I run a test so Dagger can generate the DaggerTestAppComponent but it doesn't generate a DaggerTestAppComponent.create() creating an unresolved reference in the code.

DaggerTestAppComponent

@SuppressWarnings({
    "unchecked",
    "rawtypes"
})
public final class DaggerTestAppComponent implements TestAppComponent {
  private Provider<MyRepository> myRepositoryProvider;

  private Provider<Context> contextProvider;

  private Provider<MyDatabase> provideDatabaseProvider;

  private Provider<ItemDao> provideDaoProvider;

  private Provider<RoomRepository> roomRepositoryProvider;

  private Provider<CoroutineDispatcher> providesIoDispatcherProvider;

  private Provider<MainViewModel> mainViewModelProvider;

  private DaggerTestAppComponent(DispatchersModule dispatchersModuleParam, Context contextParam) {

    initialize(dispatchersModuleParam, contextParam);
  }

  public static TestAppComponent.Factory factory() {
    return new Factory();
  }

  private MyAdapter myAdapter() {
    return new MyAdapter(mainViewModelProvider.get());
  }

  @SuppressWarnings("unchecked")
  private void initialize(final DispatchersModule dispatchersModuleParam,
      final Context contextParam) {
    this.myRepositoryProvider = MyRepository_Factory.create((Provider) FakeSaveData_Factory.create());
    this.contextProvider = InstanceFactory.create(contextParam);
    this.provideDatabaseProvider = DoubleCheck.provider(RoomModule_Companion_ProvideDatabaseFactory.create(contextProvider));
    this.provideDaoProvider = DoubleCheck.provider(RoomModule_Companion_ProvideDaoFactory.create(provideDatabaseProvider));
    this.roomRepositoryProvider = RoomRepository_Factory.create(provideDaoProvider);
    this.providesIoDispatcherProvider = DispatchersModule_ProvidesIoDispatcherFactory.create(dispatchersModuleParam);
    this.mainViewModelProvider = DoubleCheck.provider(MainViewModel_Factory.create(myRepositoryProvider, roomRepositoryProvider, providesIoDispatcherProvider));
  }

  @Override
  public void inject(MainActivity arg0) {
    injectMainActivity(arg0);
  }

  @Override
  public void inject(ListFragment arg0) {
    injectListFragment(arg0);
  }

  private MainActivity injectMainActivity(MainActivity instance) {
    MainActivity_MembersInjector.injectViewModel(instance, mainViewModelProvider.get());
    return instance;
  }

  private ListFragment injectListFragment(ListFragment instance) {
    ListFragment_MembersInjector.injectRvAdapter(instance, myAdapter());
    return instance;
  }

  private static final class Factory implements TestAppComponent.Factory {
    @Override
    public TestAppComponent create(Context context) {
      Preconditions.checkNotNull(context);
      return new DaggerTestAppComponent(new DispatchersModule(), context);
    }
  }
}

If I don't have a room installed in the app Dagger will generate DaggerTestAppComponent.create(). It's once setup room Dagger stops generating DaggerTestAppComponent.create().

Dagger dependencies

    def dagger_version = "2.33"
    implementation "com.google.dagger:dagger:$dagger_version"
    kapt "com.google.dagger:dagger-compiler:$dagger_version"
    kapt "com.google.dagger:dagger-android-processor:$dagger_version"
    kaptAndroidTest "com.google.dagger:dagger-compiler:$dagger_version"

Build output

> Task :app:compileDebugAndroidTestKotlin FAILED
e: C:\Android\TestingEx\EndToEnd\app\src\androidTest\java\com\example\endtoend\TestApplication.kt: (14, 39): Unresolved reference: create
Shawn
  • 1,222
  • 1
  • 18
  • 41
  • do you have error during the build ? – Ruokki Jun 16 '21 at 09:12
  • @Ruokki its an unresolved reference to `DaggerTestAppComponent.create()`. Which should be generated when `DaggerTestAppcomponeont` is generated. – Shawn Jun 16 '21 at 15:45
  • Yes but in most of the case when DaggerXXXComponent class didn't appear you will have an error log in the build who explain what is wrong with your dagger component (Circular reference/ not provided dependency/ etc ) – Ruokki Jun 16 '21 at 20:37
  • @Ruokki, I've included the build output to the question. – Shawn Jun 16 '21 at 22:45

1 Answers1

1

There appears to be two problems with your Dagger AndroidTest setup.

The TestApplication is calling a non existent function.

class TestApplication : MyApplication() {

override fun initializeComponent(): AppComponent {
    return DaggerTestAppComponent.create()
} }

You want to return DaggerTestAppComponent.factory().create(), you can see it at the bottom of the generated DaggerTestAppComponent file.

return DaggerTestAppComponent.factory().create(this)

Number two In the TestAppComponent the create() function returns the dagger AppComponent graph, you'll want to change that so it returns the TestAppComponent graph.

@Singleton
@Component(modules = [
    TestSaveDataStorageModule::class,
    RoomModule::class
])
interface TestAppComponent : AppComponent {


    @Component.Factory
    interface Factory {
        fun create(@BindsInstance context: Context): TestAppComponent
    }
}
helper
  • 100
  • 6