Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Not quite the easiest way to override Dagger 2 modules for unit tests...
@Component(modules=MyModule.class)
@ApplicationScope
public interface MyComponent {
void inject(MyApplication application);
}
@Module
public class MyModule {
@Provides @ApplicationScope
public String provideString() {
return "real string";
}
}
public class MyApplication extends Application {
@Inject public String injectedString;
private MyComponent component;
public void setComponent(MyComponent component) {
this.component = component;
component.inject(this);
Log.d("Injected: " + injectedString);
}
@Override
public void onCreate() {
super.onCreate();
if (component == null) {
setComponent(DaggerMyComponent.create());
}
}
}
public class MyActivityTest {
MyApplication app;
@Before
public void setUp() {
app = (MyApplication) getInstrumentation().getTargetContext().getApplicationContext();
// This is the sneaky bit. Create a partial mock of the Module you want to override,
// then just mock the provider methods you want to override.
MyModule module = Mockito.spy(new MyModule());
Mockito.doReturn("mocked string").when(module).provideString();
MyComponent component = DaggerMyComponent.builder()
.myModule(module)
.build();
app.setComponent(component);
}
@Test
public void mock_injected_successfully() {
assertEquals("mocked string", app.injectedString);
}
}
@tbroyer

This comment has been minimized.

Copy link

@tbroyer tbroyer commented Aug 27, 2015

…or without Mockito:

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

That doesn't work that well when your provider method has dependencies though…

See also https://groups.google.com/d/msg/dagger-discuss/FtbWILcoqHM/GUUe792rjnAJ (won't work if you use subcomponents though)

@vaughandroid

This comment has been minimized.

Copy link
Owner Author

@vaughandroid vaughandroid commented Aug 27, 2015

@tbroyer You can't override provider methods, or it would be that easy...

@vaughandroid

This comment has been minimized.

Copy link
Owner Author

@vaughandroid vaughandroid commented Aug 27, 2015

@tbroyer Actually tried out your suggestion, and I stand corrected! The overriding only fails (at compile time) if you tag it with @provides.

What sort of issues do you run into when your provider methods have dependencies?

@mheras

This comment has been minimized.

Copy link

@mheras mheras commented Aug 27, 2015

... and you still need to call setComponent. You are making that method public in your production code just for testing purposes... That's bad from a design point of view.

@vaughandroid

This comment has been minimized.

Copy link
Owner Author

@vaughandroid vaughandroid commented Aug 27, 2015

@mheras You've got to get a different Component instance in there somehow. I've seen options using build flavors which don't involve modifying the production code, but I'm happy to be pragmatic about it. My personal feeling is "testing trumps encapsulation".

@vaughandroid

This comment has been minimized.

Copy link
Owner Author

@vaughandroid vaughandroid commented Sep 11, 2015

I've posted a new gist here using @tbroyer's idea: https://gist.github.com/vaughandroid/00287633102af6a66dc8

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.