Dagger2 Inject Unit Tests является нулевым
Привет, я использовал кинжал для инъекций зависимостей сетевого модуля, ApplicationModule, DatabaseModule, презентаторов и интерактора в моем приложении. Я хочу использовать эти же классы и модуль во время модульного тестирования.
В качестве ссылки на модульное тестирование я создал AndroidTestAppComponent, используя следующий код:
@Singleton
@Component(modules = {
AndroidSupportInjectionModule.class,
AndroidTestAppModule.class,
NetworkModule.class
})
public interface AndroidTestAppComponent extends AndroidInjector<AndroidTestApplication> {
@Component.Builder
abstract class AndroidTestAppComponentBuilder extends Builder<AndroidTestApplication> {
}
}
Предоставление всего модуля выходит за рамки этого вопроса, рассмотрим 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);
}
}
Я могу генерировать DaggerAndroidTestAppComponent
, Мой класс приложений, как показано ниже:
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;
}
}
Несколько других AppPref.java
учебный класс
@Singleton
public class AppPref {
private SharedPreferences preferences;
@Inject
AppPref(Context context) {
preferences = context.getSharedPreferences("somefile", Activity.MODE_PRIVATE);
}
}
Как читается из документации: AndroidInjection#inject(T t)
т здесь принимает основной модуль Android, поэтому, когда я вызываю это в моей деятельности AndroidInjection.inject(activity_reference_usually__this__)
это работает (нормальный сценарий, реальная сборка и не тестирование приложения)
Не изменяя много кода, как я могу использовать эти классы в AndroidInstrumentationTest, потому что я буду менять только тестовую реализацию в Test**DaggerModules
внутри тестового пакета.
Пример кода для приборов приведен ниже:
@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);
}
}
В идеале apppref - это всегда null, потому что setUp
Метод, который я ввел класс AndroidTestApplication, а не в ExampleInstrumentedTest
Как я могу отредактировать мой код dagger2 так, чтобы @Inject работал нормально, и я получил действительный объект appPref. Спасибо.
2 ответа
Вы на самом деле ничего не вводите в свой тестовый класс. DaggerAndroidTestAppComponent.builder().create(application).inject(application);
Вы вводите в AndroidTestApplication
вместо вашего теста.
Попробуй добавить
void inject(ExampleInstrumentedTest test);
В интерфейс вашего компонента.
@Override
protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
androidInjector = DaggerAndroidTestAppComponent.builder().create(this);
return androidInjector;
}
Здесь вы создаете свой Dagger Component, нет необходимости делать это снова в тесте. Делать androidInjector
быть AndroidTestAppComponent
вместо AndroidInjector
в вашем AndroidTestApplicaiton
, сделайте добытчик для этого компонента в вашем AndroidTestApplication
а затем в вашем тесте setUp
использование метода application.getComponent().inject(this);
Таким образом, вы вводите зависимости в нужный класс, который является вашим тестом.
Я должен был изменить @Component
интерфейс для пропуска расширяющего компоновщика из AndroidInjector.Builder
и предоставить свой собственный подход.
@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();
}
}
Так что мне пришлось вручную передать приложение и собрать компонент, а затем, как предложено Туби, мне пришлось добавить новый метод void inject(ExampleInstrumentedTest test)
в @Component
интерфейс.
Мой тестовый класс теперь выглядит следующим образом, и я могу запустить тест и получить покрытие [инструмент jacoco]:
@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());
}
}