Reputation: 6973
Hi i have used dagger for dependency injections of Network Module, ApplicationModule, DatabaseModule, Presenters and interactor in my app. I want to use these same classes and Module during unit testing.
As unit testing reference, i have created AndroidTestAppComponent using following code:
@Singleton
@Component(modules = {
AndroidSupportInjectionModule.class,
AndroidTestAppModule.class,
NetworkModule.class
})
public interface AndroidTestAppComponent extends AndroidInjector<AndroidTestApplication> {
@Component.Builder
abstract class AndroidTestAppComponentBuilder extends Builder<AndroidTestApplication> {
}
}
Giving all module is out of scope for this question, consider AndroidTestAppModule.java below
:
public class AndroidTestAppModule {
@Provides
@Singleton
Context provideContext(AndroidTestApplication application) {
return application.getApplicationContext();
}
@Singleton
@Provides
KeyguardManager provideKeyguardManager(Context context) {
return (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
}
@Singleton
@Provides
FingerprintManagerCompat providerFingerPrintManager(Context context) {
return FingerprintManagerCompat.from(context);
}
}
I am able to generate DaggerAndroidTestAppComponent
.
My Application class is as below:
public class AndroidTestApplication extends DaggerApplication implements HasActivityInjector {
@Inject
DispatchingAndroidInjector<Activity> dispatchingActivityInjector;
AndroidInjector<AndroidTestApplication> androidInjector;
@Override
public void onCreate() {
super.onCreate();
androidInjector.inject(this);
}
@Override
protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
androidInjector = DaggerAndroidTestAppComponent.builder().create(this);
return androidInjector;
}
@Override
public DispatchingAndroidInjector<Activity> activityInjector() {
return dispatchingActivityInjector;
}
}
Some other AppPref.java
class
@Singleton
public class AppPref {
private SharedPreferences preferences;
@Inject
AppPref(Context context) {
preferences = context.getSharedPreferences("somefile", Activity.MODE_PRIVATE);
}
}
As read from documentation: AndroidInjection#inject(T t)
t here takes core android module, so when i call this in my Activity AndroidInjection.inject(activity_reference_usually__this__)
it works(Normal scenario, real build and no testing app)
Without changing much code how can i use these Classes in AndroidInstrumentationTest, because i will only change test implementation in Test**DaggerModules
inside test package.
Sample code for instrumentation is given below:
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
AndroidTestApplication application;
@Inject
AppPref appPref;
@Before
public void setUp() throws IllegalAccessException, InstantiationException, ClassNotFoundException {
Context appContext = InstrumentationRegistry.getTargetContext();
application = (AndroidTestApplication) Instrumentation.newApplication(AndroidTestApplication.class, appContext);
DaggerAndroidTestAppComponent.builder().create(application).inject(application);
}
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("com.a.b", appContext.getPackageName());
}
@Test
public void testPreNotNUll() {
Assert.assertNotNull(appPref);
}
}
Ideally, apppref is alwyas null, becuase in setUp
method i have injected AndroidTestApplication class and not in ExampleInstrumentedTest
how can i edit my dagger2 code so that @Inject works fine and i get valid appPref object.
Thank you.
Upvotes: 2
Views: 1868
Reputation: 6973
I had to modify @Component
interface to skip extending builder from AndroidInjector.Builder
and provide my own approach.
@Singleton
@Component(modules = {
AndroidSupportInjectionModule.class,
AndroidTestAppModule.class,
NetworkModule.class
})
public interface AndroidTestAppComponent extends AndroidInjector<AndroidTestApplication> {
void inject(ExampleInstrumentedTest test);
@Component.Builder
abstract class AndroidTestAppComponentBuilder {
@BindsInstance
public abstract AndroidTestAppComponentBuilder application(AndroidTestApplication application);
public abstract AndroidTestAppComponent build();
}
}
Such that i had to manually pass application and build the component, then as suggested by tuby, i had to add new method void inject(ExampleInstrumentedTest test)
to @Component
interface.
My test class now looks like this and i am able to run test and get coverage[jacoco tool]:
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Inject
AppPref appPref;
@Before
public void setUp() throws IllegalAccessException, InstantiationException, ClassNotFoundException {
Context appContext = InstrumentationRegistry.getTargetContext();
AndroidTestApplication application = (AndroidTestApplication) Instrumentation
.newApplication(AndroidTestApplication.class, appContext);
DaggerAndroidTestAppComponent.builder().application(application)
.build()
.inject(this);
}
@Test
public void test1AppPrefNotNUll() {
Assert.assertNotNull(appPref);
}
private final String KEY = "key";
private final String valid = "test_app";
private final String invalid = "non_app";
@Test
public void test2AppPrefWrite() {
appPref.writePreference(KEY, valid);
Assert.assertNotNull(appPref.readPreference(KEY));
}
@Test
public void test3AppPrefRead() {
Assert.assertEquals(valid, appPref.readPreference(KEY));
}
@Test
public void test4AppPrefInvalid() {
Assert.assertNotNull(invalid, appPref.readPreference(KEY));
}
@Test
public void test5AppPrefClear() {
appPref.clearPreferences();
Assert.assertEquals(0, appPref.size());
}
}
Upvotes: 0
Reputation: 3253
You are actually not injecting anything into your Test class.
DaggerAndroidTestAppComponent.builder().create(application).inject(application);
You are injecting into AndroidTestApplication
instead of your Test.
Try to add
void inject(ExampleInstrumentedTest test);
Into your Component interface.
@Override
protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
androidInjector = DaggerAndroidTestAppComponent.builder().create(this);
return androidInjector;
}
Here you are creating your Dagger Component, no need to do it again in the Test.
Make androidInjector
to be AndroidTestAppComponent
instead of AndroidInjector
in your AndroidTestApplicaiton
, make a getter for that Component in your AndroidTestApplication
and then in your Test setUp
method use application.getComponent().inject(this);
That way you are injecting dependencies into desired class which is your Test.
Upvotes: 1