66

I have a simple Android activity with a single dependency. I inject the dependency into the activity's onCreate like this:

Dagger_HelloComponent.builder()
    .helloModule(new HelloModule(this))
    .build()
    .initialize(this);

In my ActivityUnitTestCase I want to override the dependency with a Mockito mock. I assume I need to use a test-specific module which provides the mock, but I can't figure out how to add this module to the object graph.

In Dagger 1.x this is apparently done with something like this:

@Before
public void setUp() {
  ObjectGraph.create(new TestModule()).inject(this);
}

What's the Dagger 2.0 equivalent of the above?

You can see my project and its unit test here on GitHub.

vepzfe
  • 4,217
  • 5
  • 26
  • 46
G. Lombard
  • 3,569
  • 1
  • 29
  • 30
  • 4
    Here's a video by Jake Wharton from Devoxx 2014: https://plus.google.com/+JakeWharton/posts/SRaaHenwLfj in which he mentions (at 0:45:40) that Module Overrides is not supported yet at time of the presentation. – G. Lombard Jan 14 '15 at 21:38
  • 2
    Follow the discussion here: https://github.com/google/dagger/issues/110 – G. Lombard Jul 10 '15 at 04:02

9 Answers9

51

Probably this is more a workaround that proper support for test module overriding, but it allows to override production modules with test one. The code snippets below shows simple case when you have just one component and one module, but this should work for any scenario. It requires a lot of boilerplate and code repetition so be aware of this. I'm sure there'll be a better way to achieve this in the future.

I've also created a project with examples for Espresso and Robolectric. This answer is based on code contained in the project.

The solution requires two things:

  • provide additional setter for @Component
  • test component must extend the production component

Assume we've simple Application like below:

public class App extends Application {

    private AppComponent mAppComponent;

    @Override
    public void onCreate() {
        super.onCreate();
        mAppComponent = DaggerApp_AppComponent.create();
    }

    public AppComponent component() {
        return mAppComponent;
    }

    @Singleton
    @Component(modules = StringHolderModule.class)
    public interface AppComponent {

        void inject(MainActivity activity);
    }

    @Module
    public static class StringHolderModule {

        @Provides
        StringHolder provideString() {
            return new StringHolder("Release string");
        }
    }
}

We've to add additional method to App class. This allows us to replace the production component.

/**
 * Visible only for testing purposes.
 */
// @VisibleForTesting
public void setTestComponent(AppComponent appComponent) {
    mAppComponent = appComponent;
}

As you can see the StringHolder object contains "Release string" value. This object is injected to the MainActivity.

public class MainActivity extends ActionBarActivity {

    @Inject
    StringHolder mStringHolder;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ((App) getApplication()).component().inject(this);
    }
}

In our tests we want to provide StringHolder with "Test string". We've to set the test component in App class before the MainActivity is created - because StringHolder is injected in the onCreate callback.

In Dagger v2.0.0 components can extend other interfaces. We can leverage this to create our TestAppComponent which extends AppComponent.

@Component(modules = TestStringHolderModule.class)
interface TestAppComponent extends AppComponent {

}

Now we're able to define our test modules e.g. TestStringHolderModule. The last step is to set the test component using previously added setter method in App class. It's important to do this before the activity is created.

((App) application).setTestComponent(mTestAppComponent);

Espresso

For Espresso I've created custom ActivityTestRule which allows to swap the component before the activity is created. You can find code for DaggerActivityTestRule here.

Sample test with Espresso:

@RunWith(AndroidJUnit4.class)
@LargeTest
public class MainActivityEspressoTest {

    public static final String TEST_STRING = "Test string";

    private TestAppComponent mTestAppComponent;

    @Rule
    public ActivityTestRule<MainActivity> mActivityRule =
            new DaggerActivityTestRule<>(MainActivity.class, new OnBeforeActivityLaunchedListener<MainActivity>() {
                @Override
                public void beforeActivityLaunched(@NonNull Application application, @NonNull MainActivity activity) {
                    mTestAppComponent = DaggerMainActivityEspressoTest_TestAppComponent.create();
                    ((App) application).setTestComponent(mTestAppComponent);
                }
            });

    @Component(modules = TestStringHolderModule.class)
    interface TestAppComponent extends AppComponent {

    }

    @Module
    static class TestStringHolderModule {

        @Provides
        StringHolder provideString() {
            return new StringHolder(TEST_STRING);
        }
    }

    @Test
    public void checkSomething() {
        // given
        ...

        // when
        onView(...)

        // then
        onView(...)
                .check(...);
    }
}

Robolectric

It's much easier with Robolectric thanks to the RuntimeEnvironment.application.

Sample test with Robolectric:

@RunWith(RobolectricGradleTestRunner.class)
@Config(emulateSdk = 21, reportSdk = 21, constants = BuildConfig.class)
public class MainActivityRobolectricTest {

    public static final String TEST_STRING = "Test string";

    @Before
    public void setTestComponent() {
        AppComponent appComponent = DaggerMainActivityRobolectricTest_TestAppComponent.create();
        ((App) RuntimeEnvironment.application).setTestComponent(appComponent);
    }

    @Component(modules = TestStringHolderModule.class)
    interface TestAppComponent extends AppComponent {

    }

    @Module
    static class TestStringHolderModule {

        @Provides
        StringHolder provideString() {
            return new StringHolder(TEST_STRING);
        }
    }

    @Test
    public void checkSomething() {
        // given
        MainActivity mainActivity = Robolectric.setupActivity(MainActivity.class);

        // when
        ...

        // then
        assertThat(...)
    }
}
tomrozb
  • 25,773
  • 31
  • 101
  • 122
  • 2
    The only "problem" with this is that I can't extends StringHolderModule. So if my module have a lot of providers, I need to copy/paste and mock only the one I want. Other than that, this answers is cooooorrect! /o/ – Tsuharesu May 18 '15 at 20:16
  • 2
    @Tsuharesu "and mock only the one I want" - maybe you should decouple your modules? It's a 'bad smell' to keep all provides methods in just one module. It prevents code reuse. Create a module per different kinds of dependencies and you will easily get rid of this problem. – tomrozb May 18 '15 at 20:22
  • my AppModule, for example, have things that depends on the app context, like SharedPreferences and the actual app instance (so I can initialize the Prefs). So even this tiny module have 2 provides, and 1 I'll need to mock. Either way, I'm starting learn more about Dagger and DI in general, thanks for the tip :) – Tsuharesu May 20 '15 at 02:28
  • @PaulTurchenko why? Could you please explain? – tomrozb Jul 02 '15 at 20:50
  • 2
    It's ok, but you are adding a public setter in your `Application` class just for testing purposes... That's also your production code, and you are exposing things just for testing. Dagger 2 has no support for testing yet, and that's why we are all trying to find the cleanest workaround. I would simply stick to Dagger 1 until Google fixes it. – mato Jul 18 '15 at 05:47
  • @mato Of course it's bad but this is the only possible solution so far. You can always configure Proguard to strip this additional method from release builds so you are sure no one is using it (otherwise the app would crash). – tomrozb Jul 18 '15 at 23:07
  • @mato I agree that exposing a public setter in production `Application` just for testing purposes is bad practice. See my answer below for a workaround that doesn't require doing that – yuval Aug 14 '15 at 23:37
  • I can not put the `TestAppComponent` class inside src/test folder, I have to put it in src/main folder. Otherwise the DaggerTestAppComponent cannot be generated. Any ideas why? – Chris.Zou Aug 26 '15 at 09:11
  • @Chris.Zou This is bug in Android gradle plugin. It may be fixed in 1.4.0. In fact this class is generated but isn't visible in Android Studio. The code compiles and works, you can check to example project I've mentioned in the answer. – tomrozb Aug 26 '15 at 10:18
  • @tomrozb Thank you for the information. That bothered me a lot this afternoon. – Chris.Zou Aug 26 '15 at 15:03
  • @VisibleForTesting doesn't imply to be public. Your field could be protected. And if you move your setter at the activity level, you can still call it before onCreate via Robolectric and with the rule for Espresso and it makes it cleaner, less polluting centrally the app. http://blog.sqisland.com/2015/04/dagger-2-espresso-2-mockito.html – Snicolas Nov 05 '15 at 05:32
25

As @EpicPandaForce rightly says, you can't extend Modules. However, I came up with a sneaky workaround for this which I think avoids a lot of the boilerplate which the other examples suffer from.

The trick to 'extending' a Module is to create a partial mock, and mock out the provider methods which you want to override.

Using Mockito:

MyModule module = Mockito.spy(new MyModule());
Mockito.doReturn("mocked string").when(module).provideString();

MyComponent component = DaggerMyComponent.builder()
        .myModule(module)
        .build();

app.setComponent(component);

I created this gist here to show a full example.

EDIT

It turns out you can do this even without a partial mock, like so:

MyComponent component = DaggerMyComponent.builder()
        .myModule(new MyModule() {
            @Override public String provideString() {
                return "mocked string";
            }
        })
        .build();

app.setComponent(component);
vaughandroid
  • 4,315
  • 1
  • 27
  • 33
11

The workaround proposed by @tomrozb is very good and put me on the right track, but my problem with it was that it exposed a setTestComponent() method in the PRODUCTION Application class. I was able to get this working slightly differently, such that my production application doesn't have to know anything at all about my testing environment.

TL;DR - Extend your Application class with a test application that uses your test component and module. Then create a custom test runner that runs on the test application instead of your production application.


EDIT: This method only works for global dependencies (typically marked with @Singleton). If your app has components with different scope (e.g. per activity) then you'll either need to create subclasses for each scope, or use @tomrozb's original answer. Thanks to @tomrozb for pointing this out!


This example uses the AndroidJUnitRunner test runner but this could probably be adapted to Robolectric and others.

First, 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();
    }
}

This way, my activities and other class that use @Inject simply have to call something like getApp().getComponent().inject(this); to inject themselves into the dependency graph.

For completeness, here is my component:

@Singleton
@Component(modules = {MyModule.class})
public interface MyComponent {
    void inject(MyApp app);
    // other injects and getters
}

And my module:

@Module
public class MyModule {
    // EDIT: This solution only works for global dependencies
    @Provides @Singleton
    public MyClass provideMyClass() { ... }

    // ... other providers
}

For the testing environment, extend your test component from your production component. This is the same as in @tomrozb's answer.

@Singleton
@Component(modules = {MyTestModule.class})
public interface MyTestComponent extends MyComponent {
    // more component methods if necessary
}

And the test module can be whatever you want. Presumably you'll handle your mocking and stuff in here (I use 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.
}

So now, the tricky part. Create a test application class that extends from your production application class, and override the setComponent() method to set the test component with the test module. Note that this can only work if MyTestComponent is a descendant of 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)
    }
}

Make sure you call setComponent() on the app before you begin your tests to make sure the graph is set up correctly. Something like this:

@Before
public void setUp() {
    MyTestApp app = (MyTestApp) getInstrumentation().getTargetContext().getApplicationContext();
    app.setComponent()
    ((MyTestComponent) app.getComponent()).inject(this)
}

Finally, the last missing piece is to override your TestRunner with a custom test runner. In my project I was using the 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'll also have to update your testInstrumentationRunner gradle, like so:

testInstrumentationRunner "com.mypackage.TestRunner"

And if you're using Android Studio, you'll also have to click Edit Configuration from the run menu and enter the name of your test runner under "Specific instrumentation runner".

And that's it! Hopefully this information helps somebody :)

yuval
  • 6,369
  • 3
  • 32
  • 44
  • What about components with different scope (e.g. per activity)? This only works for components that are created in Application object, thus solves problems for simple apps. In bigger apps that uses other scopes you'll still need to use public setter. I'm not saying this solution is wrong, but it doesn't cover all use cases. – tomrozb Aug 17 '15 at 07:02
  • 2
    This is IMO the cleanest way for the time being (although I prefer to override `getComponent` rather than having a `setComponent`, even private, but the result is the same). *For robolectric-runned tests*, one can specify the application class by annotating the test class or method with `@Config(application = MyTestApp.class)`. – desseim Aug 17 '15 at 11:56
  • @tomrozb How so ? A typical injected activity, as per your example, will fetch the application component from its application context by itself, thus getting the test application component when run in a test (as per this answer's solution). – desseim Aug 17 '15 at 12:02
  • @desseim I'm not talking about global dependencies which are typically marked with Singleton. Consider fragment which needs LoginModule. This module is provided by activity scoped component that's created in LoginActivity. You don't need this component outside this activity thus it isn't global. To change this component for tests you can provide public setter in LoginActivity class. – tomrozb Aug 17 '15 at 12:13
  • @tomrozb In this case you can subclass `LoginActivity` with a `TestLoginActivity` which instantiate a test component and which you would test against, just as for the application. – desseim Aug 17 '15 at 14:28
  • @desseim I was sure that would be the answer. Are you going to create 10 subclasses if you need 10 different behaviors of the component during tests? For me creating subclasses is even worse than creating a simple setter. Both solutions ARE WRONG in terms of PURE TESTABLE CODE. We should wait for proper component override support in Dagger 2. In the mean time there are a lot of different approaches to replace the component and I'm not going to convince anybody to use setter over subclasses. My choice is a setter, because it's simple and easy to remove in the future. – tomrozb Aug 17 '15 at 16:00
  • @tomrozb I was just trying to understand your comment that it would "only work for components that are created in Application object" and why "you [would] still need to use public setter". Thanks for clarifying! – desseim Aug 17 '15 at 16:18
  • @tomrozb you're right, I didn't realize that this doesn't cover all cases (without making a bunch of subclasses). Indeed, in the app in this example I was only using global Singleton dependencies. I'm gonna update my answer to reflect this limitation. Thank you! – yuval Aug 17 '15 at 20:55
  • 2
    In my case, `DaggerMyTestComponent` cannot be generated if I put the file under src/test. I have to put it under src/main, with the production code. This is really odd. Any ideas why? – Chris.Zou Aug 26 '15 at 09:18
  • This approach is also cleaner if you have `SubComponents`. Otherwise, you'd have to create setter for each of them. – mbmc Jun 15 '16 at 00:24
  • @Chris.Zou I have the exact same problem. any solution yet? Thanks – Rotem Slootzky Jul 18 '16 at 12:34
  • 2
    @RotemSlootzky Yes, the solution is to add `testApt 'com.google.dagger:dagger-compiler:2.0.2'` to your gradle file. This way, the apt process will also work for the test source code. – Chris.Zou Jul 19 '16 at 06:54
2

It seems I've found yet another way and it's working so far.

First, a component interface that is not a component itself:

MyComponent.java

interface MyComponent {
    Foo provideFoo();
}

Then we have two different modules: actual one and testing one.

MyModule.java

@Module
class MyModule {
    @Provides
    public Foo getFoo() {
        return new Foo();
    }
}

TestModule.java

@Module
class TestModule {
    private Foo foo;
    public void setFoo(Foo foo) {
        this.foo = foo;
    }

    @Provides
    public Foo getFoo() {
        return foo;
    }
}

And we have two components to use these two modules:

MyRealComponent.java

@Component(modules=MyModule.class)
interface MyRealComponent extends MyComponent {
    Foo provideFoo(); // without this dagger will not do its magic
}

MyTestComponent.java

@Component(modules=TestModule.class)
interface MyTestComponent extends MyComponent {
    Foo provideFoo();
}

In application we do this:

MyComponent component = DaggerMyRealComponent.create();
<...>
Foo foo = component.getFoo();

While in test code we use:

TestModule testModule = new TestModule();
testModule.setFoo(someMockFoo);
MyComponent component = DaggerMyTestComponent.builder()
    .testModule(testModule).build();
<...>
Foo foo = component.getFoo(); // will return someMockFoo

The problem is that we have to copy all methods of MyModule into TestModule, but it can be done by having MyModule inside TestModule and use MyModule's methods unless they are directly set from outside. Like this:

TestModule.java

@Module
class TestModule {
    MyModule myModule = new MyModule();
    private Foo foo = myModule.getFoo();
    public void setFoo(Foo foo) {
        this.foo = foo;
    }

    @Provides
    public Foo getFoo() {
        return foo;
    }
}
aragaer
  • 17,238
  • 6
  • 47
  • 49
1

THIS ANSWER IS OBSOLETE. READ BELOW IN EDIT.

Disappointingly enough, you cannot extend from a Module, or you'll get the following compilation error:

Error:(24, 21) error: @Provides methods may not override another method.
Overrides: Provides 
    retrofit.Endpoint hu.mycompany.injection.modules.application.domain.networking.EndpointModule.mySe‌​rverEndpoint()

Meaning you can't just extend a "mock module" and replace your original module. Nope, it's not that easy. And considering you design your components in such a way that it directly binds the Modules by class, you can't really just make a "TestComponent" either, because that'd mean you have to reinvent everything from scratch, and you'd have to make a component for every variation! Clearly that is not an option.

So on the smaller scale, what I ended up doing is making a "provider" that I give to the module, which determines whether I select the mock or the production type.

public interface EndpointProvider {
    Endpoint serverEndpoint();
}

public class ProdEndpointProvider implements EndpointProvider {

    @Override
    public Endpoint serverEndpoint() {
        return new ServerEndpoint();
    }
}


public class TestEndpointProvider implements EndpointProvider {
    @Override
    public Endpoint serverEndpoint() {
        return new TestServerEndpoint();
    }
}

@Module
public class EndpointModule {
    private Endpoint serverEndpoint;

    private EndpointProvider endpointProvider;

    public EndpointModule(EndpointProvider endpointProvider) {
        this.endpointProvider = endpointProvider;
    }

    @Named("server")
    @Provides
    public Endpoint serverEndpoint() {
        return endpointProvider.serverEndpoint();
    }
}

EDIT: Apparently as the error message says, you CAN'T override another method using a @Provides annotated method, but that doesn't mean you can't override an @Provides annotated method :(

All that magic was for naught! You can just extend a Module without putting @Provides on the method and it works... Refer to @vaughandroid 's answer.

EpicPandaForce
  • 79,669
  • 27
  • 256
  • 428
1

For me the following works best.

That's not a test-friendly solution but I use it a lot to mock some APIs while developing whenever backend is not ready yet but I need to implement UI in advance.

Dagger (2.29.1) won't allow to override providing methods in modules with:

Binding methods may not be overridden in modules

However with some extra boilerplate you could cheat it:

@Module
open class NetworkServicesModule {

    /**
     * Provide a mock instead
     */
    protected open fun doProvideFlightService(context: Context, retrofit: Retrofit): FlightService {
        return retrofit.create(FlightService::class.java)
    }

    @Provides
    @Singleton
    fun provideFlightService(context: Context, retrofit: Retrofit): FlightService {
        return doProvideFlightService(context, retrofit)
    }
}


@Module
class MockNetworkServiceModule() : NetworkServicesModule() {
    /**
     * Need this to be able to mock a service in a production app
     */
    override fun doProvideFlightService(context: Context, retrofit: Retrofit): FlightService {
        return MockFlightService(context, super.doProvideFlightService(context, retrofit))
    }

Given you have a component defined like that:

@Component(
    modules = [
        NetworkServicesModule::class,
    ]
)
interface BaseComponent {
    fun provideFlightService(): FlightService

@Component.Builder
    interface Builder {
        fun networkServicesModule(networkServicesModule: NetworkServicesModule): Builder

        fun build(): BaseComponent
    }
}

You could have some network module provider which you choose depending on your app variant/flavor or the way you like it better:


/**
 * Provides mock networking services module
 */
interface NetworkServicesModuleProvider {
    /**
     * Provides networking service module
     */
    fun get() : NetworkServicesModule = NetworkServiceModule()
}

/**
 * Provides mock networking services module
 */
object MockNetworkServicesModuleProvider : NetworkServicesModuleProvider {
    /**
     * Provides networking service module
     */
    override fun get() : NetworkServicesModule = MockNetworkServiceModule()
}

Then when it's time to build your component call:


val networkServicesProvider: NetworkServicesModuleProvider = ...

DaggerBaseComponent.builder()
            .networkServicesModule(networkServicesProvider.get())
            .build()
Motorro
  • 227
  • 2
  • 10
0

Can you guys check out my solution, I have included subcomponent example: https://github.com/nongdenchet/android-mvvm-with-tests. Thank you @vaughandroid, I have borrowed your overriding methods. Here is the main point:

  1. I create a class to create subcomponent. My custom application will also hold an instance of this class:

    // The builder class
    public class ComponentBuilder {
     private AppComponent appComponent;
    
     public ComponentBuilder(AppComponent appComponent) {
      this.appComponent = appComponent;
     }
    
     public PlacesComponent placesComponent() {
      return appComponent.plus(new PlacesModule());
     }
    
     public PurchaseComponent purchaseComponent() {
      return appComponent.plus(new PurchaseModule());
     }
    }
    
    // My custom application class
    public class MyApplication extends Application {
    
     protected AppComponent mAppComponent;
     protected ComponentBuilder mComponentBuilder;
    
     @Override
     public void onCreate() {
      super.onCreate();
    
      // Create app component
      mAppComponent = DaggerAppComponent.builder()
              .appModule(new AppModule())
              .build();
    
      // Create component builder
      mComponentBuilder = new ComponentBuilder(mAppComponent);
     }
    
     public AppComponent component() {
      return mAppComponent;
     }
    
     public ComponentBuilder builder() {
      return mComponentBuilder;
     } 
    }
    
    // Sample using builder class:
    public class PurchaseActivity extends BaseActivity {
     ...    
     @Override
     protected void onCreate(Bundle savedInstanceState) {
      ...
      // Setup dependency
      ((MyApplication) getApplication())
              .builder()
              .purchaseComponent()
              .inject(this);
      ...
     }
    }
    
  2. I have a custom TestApplication that extends the MyApplication class above. This class contains two methods to replace the root component and the builder:

    public class TestApplication extends MyApplication {
     public void setComponent(AppComponent appComponent) {
      this.mAppComponent = appComponent;
     }
    
     public void setComponentBuilder(ComponentBuilder componentBuilder) {
      this.mComponentBuilder = componentBuilder;
     }
    }    
    
  3. Finally I will try to mock or stub the dependency of module and builder to provide fake dependency to the activity:

    @MediumTest
    @RunWith(AndroidJUnit4.class)
    public class PurchaseActivityTest {
    
     @Rule
     public ActivityTestRule<PurchaseActivity> activityTestRule =
         new ActivityTestRule<>(PurchaseActivity.class, true, false);
    
     @Before
     public void setUp() throws Exception {
     PurchaseModule stubModule = new PurchaseModule() {
         @Provides
         @ViewScope
         public IPurchaseViewModel providePurchaseViewModel(IPurchaseApi purchaseApi) {
             return new StubPurchaseViewModel();
         }
     };
    
     // Setup test component
     AppComponent component = ApplicationUtils.application().component();
     ApplicationUtils.application().setComponentBuilder(new ComponentBuilder(component) {
         @Override
         public PurchaseComponent purchaseComponent() {
             return component.plus(stubModule);
         }
     });
    
     // Run the activity
     activityTestRule.launchActivity(new Intent());
    }
    
Rain Vu
  • 11
  • 1
  • 3
0

I have solution for Roboletric 3.+.

I have MainActivity which i want to test without injection on create:

public class MainActivity extends BaseActivity{

  @Inject
  public Configuration configuration;

  @Inject
  public AppStateService appStateService;

  @Inject
  public LoginService loginService;

  @Override
    protected void onCreate(Bundle savedInstanceState) {
      super.processIntent(getIntent()); // this is point where pass info from test
      super.onCreate(savedInstanceState)
    ...
  }
  ...
 }

Next my BaseActivty:

public class BaseActivity extends AppCompatActivity {

  protected Logger mLog;

  protected boolean isTestingSession = false; //info about test session


  @Override
  protected void onCreate(@Nullable Bundle savedInstanceState) {
      if (!isTestingSession) { // check if it is in test session, if not enable injectig
          AndroidInjection.inject(this);
      }
      super.onCreate(savedInstanceState);
  }

  // method for receive intent from child and scaning if has item TESTING with true
  protected void processIntent(Intent intent) {
    if (intent != null && intent.getExtras() != null) {
        isTestingSession = intent.getExtras().getBoolean("TESTING", false);
    }
  }

finally my testclass:

@Before
public void setUp() throws Exception {
  ...
  // init mocks...
   loginServiceMock = mock(LoginService.class);
   locServiceMock = mock(LocationClientService.class);
   fakeConfiguration = new ConfigurationUtils(new ConfigurationXmlParser());
   fakeConfiguration.save(FAKE_XML_CONFIGURATION);
   appStateService = new AppStateService(fakeConfiguration, locServiceMock, RuntimeEnvironment.application);

   // prepare activity
   Intent intent = new Intent(RuntimeEnvironment.application, MainActivity.class);
   intent.putExtra("TESTING", true);
   ActivityController<MainActivity> activityController = Robolectric.buildActivity(MainActivity.class, intent); // place to put bundle with extras

    // get the activity instance
    mainActivity = activityController.get();


    // init fields which should be injected
    mainActivity.appStateService = appStateService;
    mainActivity.loginService = loginServiceMock;
    mainActivity.configuration = fakeConfiguration;


    // and whoala 
    // now setup your activity after mock injection
    activityController.setup();

    // get views etc..
    actionButton = mainActivity.findViewById(R.id.mainButtonAction);
    NavigationView navigationView = mainActivity.findViewById(R.id.nav_view);

  ....
  }
Andrew Sneck
  • 724
  • 9
  • 18
-5

With Dagger2, you can pass a specific module (the TestModule there) to a component using the generated builder api.

ApplicationComponent appComponent = Dagger_ApplicationComponent.builder()
                .helloModule(new TestModule())
                .build();

Please note that, Dagger_ApplicationComponent is a generated class with the new @Component annotation.

Raphaël Brugier
  • 480
  • 5
  • 14
  • 3
    This will not work. The `helloModule` method only accepts instance of `HelloModule` and since modules cannot extend from other classes `TestModule` cannot work. – Jake Wharton Nov 17 '14 at 08:33
  • Thanks Jake Wharton, I though TestModule could extend HelloModule. Is this intended or is there any open issue in dagger2 github related to this ? – Raphaël Brugier Nov 17 '14 at 12:36
  • 1
    No issue. It's a well known thing that we have been discussing though. – Jake Wharton Nov 17 '14 at 16:08
  • Thanks for the info Jake Wharton! – G. Lombard Nov 18 '14 at 22:33
  • 6
    If it's not an issue, how are you suppose to replace a service by its mocked version for a test ? By declaring another "@Component" and compose with others "@Component" dependencies ? – Raphaël Brugier Nov 20 '14 at 08:16
  • AFAIK you should create a debug version of the module, and just use it when running your tests. There you should return mocked versions of your dependencies. – Papipo Apr 18 '15 at 20:22
  • I ran in this today as well, pretty bad news, as some of my classes under test need at least mocks to run. – Kitesurfer Apr 19 '15 at 14:22
  • Error:(24, 21) error: @Provides methods may not override another method. Overrides: Provides retrofit.Endpoint hu.mycompany.injection.modules.application.domain.networking.EndpointModule.myServerEndpoint() -_- – EpicPandaForce Jun 09 '15 at 14:38