The workaround suggested by @tomrozb is very good and set me on the right path, but my problem was that it setTestComponent() method in the PRODUCTION Application class. I managed to get this to work a little differently, so my production application didn't need to know anything about my test environment.
TL; DR - Extend your application class with a test application that uses your test component and module. Then create your own test runner that runs in the test application, and not in the production application.
EDIT: This method only works for global dependencies (usually with @Singleton ). If your application has components with different scope (for example, for each action), you will need to create subclasses for each scope or use the original @tomrozb answer. Thanks to @tomrozb for pointing this out!
This example uses the AndroidJUnitRunner tester, but it could probably be adapted to Robolectric and others.
Firstly, my production application. It looks something like this:
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(); } }
So my actions and other classes that use @Inject just have to call something like getApp().getComponent().inject(this); to enter yourself in the dependency graph.
For completeness, here is my component:
@Singleton @Component(modules = {MyModule.class}) public interface MyComponent { void inject(MyApp app);
And my module:
@Module public class MyModule {
In a test environment, extend your test component from your production component. This is the same as @ tomrozb answer.
@Singleton @Component(modules = {MyTestModule.class}) public interface MyTestComponent extends MyComponent {
And the test module can be anything you want. Presumably, you will handle your bullying and things here (I use Mockito).
@Module public class MyTestModule {
So, the hard part. Create a test application class that extends from your production application class and override the setComponent() method to install the test component using the test module. Note that this can only work if MyTestComponent is a descendant of MyComponent .
public class MyTestApp extends MyApp {
Before starting the tests, make sure you call setComponent() to make sure the graph is set up correctly. Something like that:
@Before public void setUp() { MyTestApp app = (MyTestApp) getInstrumentation().getTargetContext().getApplicationContext(); app.setComponent() ((MyTestComponent) app.getComponent()).inject(this) }
Finally, the last missing element is to override your TestRunner with a special test runner. In my project, I used AndroidJUnitRunner , but it looks like you can 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); } }
You will also need to update testInstrumentationRunner gradle, for example:
testInstrumentationRunner "com.mypackage.TestRunner"
And if you use Android Studio, you also need to click "Edit Configuration" in the "Start" menu and enter the name of your test runner in the "Specific Controller" section.
And this! Hope this information helps someone :)