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
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
}
}