Dagger2 Inject Unit Tests является нулевым - PullRequest
0 голосов
/ 01 ноября 2018

Привет, я использовал кинжал для инъекций зависимостей сетевого модуля, ApplicationModule, DatabaseModule, Presenters и интерактора в моем приложении. Я хочу использовать эти же классы и модуль во время модульного тестирования.

В качестве эталонного тестирования я создал 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) 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 ]

0 голосов
/ 01 ноября 2018

Мне пришлось изменить @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());
    }

}
0 голосов
/ 01 ноября 2018

Вы на самом деле ничего не вводите в свой тестовый класс. 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, а затем в своем методе Test setUp используйте application.getComponent().inject(this); Таким образом, вы вводите зависимости в нужный класс, который является вашим тестом.

...