Currently testing with dagger, what I want to do is instantiate and inject different Bar implementations. How can I inject fields in provided fields? for example:
Module:
@Module(
injects = {
Main.class
},
complete = false,
library = true
)
public class ExampleTestModule {
@Provides
public Foo providesFoo() {
return new Foo();
}
@Provides
public Bar providesBar(BarImpl impl) {
// return new BarImpl(); // null
return impl;
}
}
Main:
public class Main {
@Inject
Foo foo;
}
Foo:
public class Foo {
@Inject
Bar bar;
}
Bar:
public interface Bar {
}
BarImpl
public class BarImpl implements Bar {
}
TestCase:
public class ApplicationTest extends ApplicationTestCase<Application> {
public ApplicationTest() {
super(Application.class);
}
public void testFoo() {
Main main = new Main();
ObjectGraph.create(new ExampleTestModule()).inject(main);
assertNotNull(main.foo);
}
public void testFooBar() {
Main main = new Main();
ObjectGraph.create(new ExampleTestModule()).inject(main);
assertNotNull(main.foo.bar);
}
}
Main.Foo is not null but Main.Foo.Bar is null.
You are never injecting bar
into foo
.
ObjectGraph.create(new ExampleTestModule()).inject(main);
This line will only look at the fields of main
which are annotated by @Inject
, and inject them. There is no recursive behavior.
Let's go step-by-step:
You provided complete = false
and library = true
in your Module
. You should only use these if really necessary. Dagger will give you warnings when something is wrong, and these properties surpress these warnings. For example, removing them raises the following warning when compiling:
Error:(11, 8) error: No injectable members on BarImpl. Do you want to add an injectable constructor? required by providesBar(BarImpl) for ExampleTestModule.
Let's add an empty injectable constructor to BarImpl
, as it suggests:
public class BarImpl implements Bar {
@Inject
BarImpl(){
}
}
Compiling will give a new error:
Error:(11, 8) error: Graph validation failed: You have these unused @Provider methods:
1. ExampleTestModule.providesBar()
Set library=true in your module to disable this check.
Apparently, providesBar()
is never used. That means, the bar
field in Foo
will never be injected. You can do two things:
Inject bar
manually:
ObjectGraph graph = ObjectGraph.create(new ExampleTestModule());
graph.inject(main);
graph.inject(main.foo);
Use injectable constructors (Preferred option):
public class Foo {
Bar bar;
@Inject
Foo(Bar bar){
this.bar = bar;
}
}
Using the injectable constructor, you will now have a compile error in providesFoo()
, since you don't supply a Bar
instance in the Foo
constructor. The nice thing about Dagger is, you can safely completely remove this method. Since Foo
is annotated with @Injectable
, everywhere it needs to inject a Foo
instance, it uses this constructor. And when it uses this constructor, it notices it needs a Bar
instance, and injects this as well.
Finally, we can remove the @Inject
annotation from the Foo
field in Main
, and create an injectable constructor. Using ObjectGraph.get(Class<?>)
we can retrieve a fully instantiated Main
instance.
The end result should look like this:
Module:
@Module(
injects = Main.class
)
public class ExampleTestModule {
@Provides
public Bar providesBar(BarImpl impl) {
return impl;
}
}
Main:
public class Main {
Foo foo;
@Inject
Main(Foo foo) {
this.foo = foo;
}
}
Foo:
public class Foo {
Bar bar;
@Inject
Foo(Bar bar){
this.bar = bar;
}
}
Bar:
public interface Bar {
}
BarImpl:
public class BarImpl implements Bar {
@Inject
BarImpl(){
}
}
ApplicationTest:
public class ApplicationTest extends ApplicationTestCase<Application> {
public ApplicationTest() {
super(Application.class);
}
public void testFoo() {
Main main = ObjectGraph.create(new ExampleTestModule()).get(Main.class);
assertNotNull(main.foo);
}
public void testFooBar() {
Main main = ObjectGraph.create(new ExampleTestModule()).get(Main.class);
assertNotNull(main.foo.bar);
}
}
From the result, we can conclude some things:
library = true
and complete = false
to your module. This should only be necessary when using multiple complex modules.private
, like they should be.providesXXX
methods when injecting instances of interfaces, like we did with Bar
and BarImpl
. Because, hey, that's exactly what Dependency Injection is for, right?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With