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.
THIS ANSWER IS OBSOLETE. READ BELOW IN EDIT.
Disappointingly enough, you cannot extend from a Module, or you'll get the following compilation error:
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.
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.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:
@Component
Assume we've simple
Application
like below:We've to add additional method to
App
class. This allows us to replace the production component.As you can see the
StringHolder
object contains "Release string" value. This object is injected to theMainActivity
.In our tests we want to provide
StringHolder
with "Test string". We've to set the test component inApp
class before theMainActivity
is created - becauseStringHolder
is injected in theonCreate
callback.In Dagger v2.0.0 components can extend other interfaces. We can leverage this to create our
TestAppComponent
which extendsAppComponent
.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 inApp
class. It's important to do this before the activity is created.Espresso
For Espresso I've created custom
ActivityTestRule
which allows to swap the component before the activity is created. You can find code forDaggerActivityTestRule
here.Sample test with Espresso:
Robolectric
It's much easier with Robolectric thanks to the
RuntimeEnvironment.application
.Sample test with Robolectric:
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:
I create a class to create subcomponent. My custom application will also hold an instance of this class:
I have a custom TestApplication that extends the MyApplication class above. This class contains two methods to replace the root component and the builder:
Finally I will try to mock or stub the dependency of module and builder to provide fake dependency to the activity:
With Dagger2, you can pass a specific module (the TestModule there) to a component using the generated builder api.
Please note that, Dagger_ApplicationComponent is a generated class with the new @Component annotation.
I have solution for Roboletric 3.+.
I have MainActivity which i want to test without injection on create:
Next my BaseActivty:
finally my testclass:
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:
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: