Sono un neofita del pugnale e recentemente ho iniziato a usare il pugnale in uno dei miei progetti, perché il concetto di gestire l'iniezione delle dipendenze in modo diverso per i test e produzione, quindi essere in grado di iniettare oggetti finti che potrei usare per i test è stato grandioso.Come progettare un'applicazione Android usando il pugnale con Testing in mente
Ho modificato la mia domanda per seguire lo stile definito nello dagger simple-android example.
Dopo aver impostato tutto, ho riscontrato problemi di iniezione e non ho potuto sovraccaricare completamente le iniezioni dall'applicazione di produzione con la logica di test.
Sto cercando consigli su come impostare questo in modo che i miei test possano effettivamente iniettare in modo differenziale con mock o altri oggetti per testare se necessario, e non essere troppo sfacciati. Attualmente, il MainActivityTest viene iniettata in modo corretto, ma quando arriviamo al MainActivity, si va al PhoneApplication e inietta usando il suo oggetto grafico
Ho incluso quello che ho qui di seguito. Qualsiasi aiuto sarebbe molto apprezzato!
Ecco il mio PhoneApplication, sulla base del 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);
}
}
Ed ecco la mia 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();
}
}
Sulla base della esempio, ho anche istituito le mie attività per usare una base di attività.
public abstract class ActionBarBaseActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((PhoneApplication) getApplication()).inject(this);
}
}
Poi nel mio MainActivity Ho il seguente
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,
"vnd.android.cursor.dir/vnd."
+ 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);
}
...
}
...
}
Questo ha funzionato grande e le iniezioni di scivolare in posizione molto velocemente, ed ero in estasi. Fino a quando non volevo fare qualche test. Il primo test che volevo eseguire era il mio MainActivity.
nel metodo onCreate
sopra, si inietta con la LocalBroadcastManager da AndroidModule, al posto di quello da MainActivityTest, perché al momento non abbiamo un modo di raccontare la PhoneApplication o le attività che dovrebbero utilizzare un diverso oggetto grafico.
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));
}
}
Questo è un test davvero semplice per iniziare. So che su onCreate registreremo un BroadcastReceiver, quindi assicuriamoci che sia registrato. Poiché il test ha il mockLocalBroadcastManager, ma l'attività utilizza LocalBroadcastManager di produzione, la verifica ha esito negativo.
Non sono un grande esperto in test strumentali. Puoi controllare cosa viene chiamato prima - 'setUp' per test o' onCreate' per attività? –
Sei sicuro che MainActivity sia stato creato e onCreate() venga chiamato? Secondo i documenti, è necessario invocare [getActivity()] (http://developer.android.com/reference/android/test/ActivityInstrumentationTestCase2.html#getActivity()) dal metodo di test per farlo. Inoltre, hai disconnesso il nome della classe del destinatario della trasmissione trasmessa e verifica che stia effettivamente utilizzando la configurazione di produzione anziché la configurazione di sostituzione che fornisce la simulazione? –