Vote count:
0
I'm new to dagger, and I recently started using dagger in one of my own projects, because the concept of being able to handle the dependency injection differently for testing and production, thus being able to inject mock objects that I could use for testing was great.
I modified my application to follow the style laid out in the dagger simple-android example.
After setting it all up, I found that there was problems with injection, and I couldn't fully overload the injections from my production application with the testing logic.
I'm looking for advice on how to set this up in a way that my tests can actually inject differentially with mocks or other objects for testing as needed, and not be too kludgy. Currently, the MainActivityTest is injected correctly, but when we get to the MainActivity, it goes to the PhoneApplication and injects using it's object graph
I've included what I have below. Any help would be greatly appreciated!
Here is my PhoneApplication, based on the DemoApplication.
public class PhoneApplication extends Application {
private ObjectGraph graph;
@Override
public void onCreate() {
super.onCreate();
graph = ObjectGraph.create(getModules().toArray());
}
protected List<Object> getModules() {
return Arrays.asList(new AndroidModule(this), new PhoneModule());
}
public void inject(Object object) {
graph.inject(object);
}
}
And here's my AndroidModule
@Module(library = true, injects = MainActivity.class)
public class AndroidModule {
private final Context context;
public AndroidModule(Context context) {
this.context = context;
}
/**
* Allow the application context to be injected but require that it be
* annotated with {@link ForApplication @Annotation} to explicitly
* differentiate it from an activity context.
*/
@Provides
@Singleton
@ForApplication
Context provideApplicationContext() {
return context;
}
@Provides
@Singleton
NotificationManager provideNotificationManager() {
return (NotificationManager) context
.getSystemService(Application.NOTIFICATION_SERVICE);
}
@Provides
@Singleton
LocalBroadcastManager provideLocalBroadcastManager() {
return LocalBroadcastManager.getInstance(context);
}
@Provides
@Singleton
ContentResolver provideContentResolver() {
return context.getContentResolver();
}
}
Based on the example, I also set up my Activities to use a base Activity.
public abstract class ActionBarBaseActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((PhoneApplication) getApplication()).inject(this);
}
}
Then within my MainActivity I have the following
public class MainActivity extends ActionBarBaseActivity {
...
@Inject
LocalBroadcastManager localBroadcastManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
try {
messageReceivedIntentFilter = new IntentFilter(
Constants.EVENT_MESSAGE_RECEIVED,
"http://ift.tt/1jmkQ4t."
+ DataProviderContract.AUTHORITY + "."
+ DataProviderContract.MESSAGES_TABLE_NAME);
localBroadcastManager.registerReceiver(messageReceiver,
messageReceivedIntentFilter);
} catch (MalformedMimeTypeException e) {
Log.e(LOG_TAG,
"An error occurred registering an Intent for EVENT_MESSAGE_RECEIVED",
e);
}
...
}
...
}
This worked great and the injections slid into place really quickly, and I was ecstatic. Until I actually wanted to do some testing. The first test I wanted to perform was on my MainActivity.
in the onCreate method above, we inject with the LocalBroadcastManager from AndroidModule, instead of the one from MainActivityTest, because we don't currently have a way of telling the PhoneApplication or the Activities that they should use a different object graph.
public class MainActivityTest extends
ActivityInstrumentationTestCase2<MainActivity> {
@Inject
NotificationManager notificationManager;
@Inject
ContentResolver contentResolver;
@Inject
MockContentResolver mockContentResolver;
@Inject
LocalBroadcastManager localBroadcastManager;
private Context context;
public MainActivityTest() {
super(MainActivity.class);
}
@Module(injects = { MainActivityTest.class, MainActivity.class }, library = true, overrides = true)
static class MockModule {
Context context;
public MockModule(Context context) {
this.context = context;
}
@Provides
@Singleton
ContentResolver provideContentResolver() {
return provideMockContentResolver();
}
@Provides
@Singleton
MockContentResolver provideMockContentResolver() {
return new MockContentResolver();
}
@Provides
@Singleton
LocalBroadcastManager provideLocalBroadcastManager() {
return Mockito.mock(LocalBroadcastManager.class);
}
}
@Override
protected void setUp() throws Exception {
System.setProperty("dexmaker.dexcache", getInstrumentation()
.getTargetContext().getCacheDir().getPath());
context = getInstrumentation().getTargetContext();
ObjectGraph graph = ObjectGraph.create(new AndroidModule(context),
new MockModule(context));
graph.inject(this);
super.setUp();
};
@MediumTest
@UiThreadTest
public void testIncomingMessageReceiver_onReceive()
throws MalformedMimeTypeException {
ArgumentCaptor<BroadcastReceiver> receiverCaptor = ArgumentCaptor
.forClass(BroadcastReceiver.class);
Mockito.verify(localBroadcastManager, Mockito.atLeastOnce())
.registerReceiver(receiverCaptor.capture(),
Mockito.any(IntentFilter.class));
}
}
This is a really simple test to get me started. I know that in the onCreate, we're going to register a BroadcastReceiver, so lets just make sure it registered. Because the test has the mockLocalBroadcastManager, but the activity uses the production LocalBroadcastManager, the verify fails.
Aucun commentaire:
Enregistrer un commentaire