Инъекция Dagger 2 в тестах Android-инструментов

#java #android #dagger-2 #instrumented-test

Вопрос:

Я пытаюсь использовать Dagger 2 для DI в своих тестах инструментов для Android. Он отлично работает для классов/действий/фрагментов в основном компоненте приложения, но в моем тестовом компоненте, похоже, отсутствуют некоторые привязки, которые я не могу найти. Любые идеи о том, как это сделать, будут оценены по достоинству. Код, который у меня есть, выглядит так:

AssetRepositoryTest

 public class AssetRepositoryTest {

    @Nested
    @DisplayName("Given a populated database")
    public class PopulatedDatabaseInstance {

        @Inject
        private TestDatabase database;

        @Inject
        private AssetRepository repository;

        @BeforeEach
        public void setup() {
            final TestApplication application = ApplicationProvider.getApplicationContext();
            application.androidInjector().inject(this);
            
            // Setup database
        }
        
        // Tests
    }
}
 

Запустив тесты инструментария, я получаю следующее исключение:

 java.lang.IllegalArgumentException: No injector factory bound for Class<AssetRepositoryTest.PopulatedDatabaseInstance>
 

И код, связанный с кинжалом, выглядит следующим образом:

Тестовый компонент

 @Singleton
@Component(modules = {
        AndroidSupportInjectionModule.class,
        TestPersistenceModule.class
})
public interface TestComponent extends AndroidInjector<TestApplication> {

    @Component.Builder
    abstract class Builder extends AndroidInjector.Builder<TestApplication> {
    }
}
 

Модуль тестируемости

 @Module(includes = TestRoomModule.class)
public abstract class TestPersistenceModule {

    @Binds
    abstract AssetRepository bindAssetRepository(final AssetRepositoryImpl repository);
}
 

TestRoomModule

 @Module
public class TestRoomModule {

    @Provides
    @Singleton
    TestDatabase provideTestDatabase(final Application application) {
        return Room.inMemoryDatabaseBuilder(application, TestDatabase.class).build();
    }

    @Provides
    @Singleton
    AssetDao provideAssetDao(final TestDatabase testDatabase) {
        return testDatabase.getAssetDao();
    }
}
 

Тестовое приложение

 public class TestApplication extends DaggerApplication {

    @Override
    protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
        return DaggerTestComponent.builder().create(this);
    }
}
 

Кроме того, у меня есть пользовательский AndroidJUnitRunner класс расширения, который переопределяет newApplication метод и возвращает TestApplication экземпляр для тестовых случаев.

Мое AssetRepositoryImpl заключается в следующем:

AssetRepositoryИмпл

 @Singleton
public class AssetRepositoryImpl extends AbstractRepository<Asset, AssetEntity> implements AssetRepository {

    @Inject
    protected WorkspaceDao workspaceDao;

    @Inject
    public AssetRepositoryImpl(final AssetDao dao, final AssetMapper mapper) {
        super(dao, mapper);
    }
}
 

Классы, которые я здесь не вставлял, имеют @Inject аннотации в своих конструкторах, и этот код правильно работает в основном приложении с соответствующими основными модулями и компонентами.

В качестве заключительной мысли, то AssetRepositoryTest.PopulatedDatabaseInstace , что создано JUnit, поэтому не создано Dagger, и, насколько мне известно, в этом, по-видимому, и заключается проблема.

Как я могу сказать Dagger, как ввести эти поля в мой тестовый класс JUnit?

Ответ №1:

Похоже, вам не хватает некоторых шагов в настройке кинжала, я включил контрольный список, который использую.

  • Создание CustomTestRunner
 class MyCustomTestRunner : AndroidJUnitRunner() {

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

 
  • Добавить в бегун для тестирования в приложение/build.gradle
 android {
    ...
    defaultConfig {
        ...
        testInstrumentationRunner "com.example.android.dagger.MyCustomTestRunner"
    }
    ...
}
 
  • kapt должен действовать на исходном наборе androidTest.
 ...
dependencies {
    ...
    kaptAndroidTest "com.google.dagger:dagger-compiler:$dagger_version"
}

 
  • Создание Тестовых Модулей
  • Создайте компонент TestAppComponent.
  • Основное приложение должно выглядеть примерно так
 open class MyApplication : Application() {

    val appComponent: AppComponent by lazy {
        initializeComponent()
    }

    open fun initializeComponent(): AppComponent {
        return DaggerAppComponent.factory().create(applicationContext)
    }
}
 
  • Тестовое приложение
 class MyTestApplication : MyApplication() {

    override fun initializeComponent(): AppComponent {
        // Creates a new TestAppComponent that injects fakes types
        return DaggerTestAppComponent.create()
    }
}
 

Если вы ищете более подробную информацию о том, что делает каждый шаг, вы можете найти эту информацию здесь.