Обходной путь, предложенный @tomrozb, очень хорош и надел меня правильный трек, но моя проблема заключалась в том, что он обнаружил setTestComponent()
метод в ПРОИЗВОДСТВЕ Application
класс. Мне удалось заставить это работать немного по-другому, так что мое производственное приложение не должно вообще ничего знать о моей тестовой среде.
TL; DR - Расширьте класс приложения с помощью тестового приложения, которое использует ваш тестовый компонент и модуль. Затем создайте собственный тестовый бегун, который запускается в тестовом приложении вместо вашего производственного приложения.
EDIT: Этот метод работает только для глобальных зависимостей (как правило, с пометкой @Singleton
). Если ваше приложение имеет компоненты с разной областью действия (например, для каждого действия), вам нужно будет либо создавать подклассы для каждой области, либо использовать исходный ответ @ tomrozb. Спасибо @tomrozb за это!
В этом примере используется AndroidJUnitRunner тестов-бегуна, но это, вероятно, может быть адаптировано к Robolectric и другим.
Во-первых, мое приложение для производства. Это выглядит примерно так:
public class MyApp extends Application {
protected MyComponent component;
public void setComponent() {
component = DaggerMyComponent.builder()
.myModule(new MyModule())
.build();
component.inject(this);
}
public MyComponent getComponent() {
return component;
}
@Override
public void onCreate() {
super.onCreate();
setComponent();
}
}
Таким образом, мои действия и другого класса, которые используют @Inject
просто назвать что-то вроде getApp().getComponent().inject(this);
вводить себя в графе зависимостей.
Для полноты, вот мой компонент:
@Singleton
@Component(modules = {MyModule.class})
public interface MyComponent {
void inject(MyApp app);
// other injects and getters
}
И мой модуль:
@Module
public class MyModule {
// EDIT: This solution only works for global dependencies
@Provides @Singleton
public MyClass provideMyClass() { ... }
// ... other providers
}
Для среды тестирования, продлить тестовый компонент вашего производственного компонента. Это то же самое, что и в ответе @ tomrozb.
@Singleton
@Component(modules = {MyTestModule.class})
public interface MyTestComponent extends MyComponent {
// more component methods if necessary
}
И тестовый модуль может быть любым, что вы хотите. Предположительно, вы будете обрабатывать свое издевательство и прочее здесь (я использую Mockito).
@Module
public class MyTestModule {
// EDIT: This solution only works for global dependencies
@Provides @Singleton
public MyClass provideMyClass() { ... }
// Make sure to implement all the same methods here that are in MyModule,
// even though it's not an override.
}
Так что теперь сложная часть. Создайте класс тестового приложения, который простирается от вашего производственного класса приложения, и переопределите метод setComponent()
, чтобы установить тестовый компонент с помощью тестового модуля. Обратите внимание, что это может работать, только если MyTestComponent
является потомком MyComponent
.
public class MyTestApp extends MyApp {
// Make sure to call this method during setup of your tests!
@Override
public void setComponent() {
component = DaggerMyTestComponent.builder()
.myTestModule(new MyTestModule())
.build();
component.inject(this)
}
}
Убедитесь, что вы звоните setComponent()
на приложение, прежде чем начать свои тесты, чтобы убедиться, что граф настроен правильно. Что-то вроде этого:
@Before
public void setUp() {
MyTestApp app = (MyTestApp) getInstrumentation().getTargetContext().getApplicationContext();
app.setComponent()
((MyTestComponent) app.getComponent()).inject(this)
}
Наконец, последний недостающий кусок, чтобы переопределить TestRunner с пользовательским тестовым бегуном. В моем проекте я использовал AndroidJUnitRunner
, но похоже, что вы можете do the same with Robolectric.
public class TestRunner extends AndroidJUnitRunner {
@Override
public Application newApplication(@NonNull ClassLoader cl, String className, Context context)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
return super.newApplication(cl, MyTestApp.class.getName(), context);
}
}
Вы также должны обновить testInstrumentationRunner
Gradle, например, так:
testInstrumentationRunner "com.mypackage.TestRunner"
И если вы используете Android Studio, вы также должны нажать Edit Configuration из меню запуска и введите имя вашего тестового бегуна в разделе «Специфический контролер».
И все! Надеюсь, эта информация помогает кому-то :)
Вот видео Джейк Wharton от Devoxx 2014: https://plus.google.com/+JakeWharton/posts/SRaaHenwLfj , в котором он упоминает (в 0:45:40), что модуль Overrides еще не поддерживается во время презентации. –
Следуйте приведенным здесь обсуждениям: https://github.com/google/dagger/issues/110 –