Ho recentemente passato intere con Dagger perché il concetto di DI ha perfettamente senso. Uno dei "sottoprodotti" più belli di DI (come ha fatto Jake Wharton in una delle sue presentazioni) è la facilità di testabilità.ottenere Dagger per iniettare oggetti finti quando si eseguono test funzionali dell'espresso per Android
Così ora sto praticamente usando espresso per fare alcuni test funzionali, e voglio essere in grado di iniettare dati fittizi/mock all'applicazione e fare in modo che l'attività li mostri. Sto indovinando poiché, questo è uno dei maggiori vantaggi di DI, questa dovrebbe essere una domanda relativamente semplice. Per qualche motivo, però, non riesco a spiegarmelo. Qualsiasi aiuto sarebbe molto apprezzato. Ecco quello che ho finora (ho scritto su un esempio che riflette il mio attuale configurazione):
public class MyActivity
extends MyBaseActivity {
@Inject Navigator _navigator;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyApplication.get(this).inject(this);
// ...
setupViews();
}
private void setupViews() {
myTextView.setText(getMyLabel());
}
public String getMyLabel() {
return _navigator.getSpecialText(); // "Special Text"
}
}
Questi sono i miei moduli pugnale:
// Navigation Module
@Module(library = true)
public class NavigationModule {
private Navigator _nav;
@Provides
@Singleton
Navigator provideANavigator() {
if (_nav == null) {
_nav = new Navigator();
}
return _nav;
}
}
// App level module
@Module(
includes = { SessionModule.class, NavigationModule.class },
injects = { MyApplication.class,
MyActivity.class,
// ...
})
public class App {
private final Context _appContext;
AppModule(Context appContext) {
_appContext = appContext;
}
// ...
}
Nella mia prova Espresso, sto cercando per inserire un modulo falso in questo modo:
public class MyActivityTest
extends ActivityInstrumentationTestCase2<MyActivity> {
public MyActivityTest() {
super(MyActivity.class);
}
@Override
public void setUp() throws Exception {
super.setUp();
ObjectGraph og = ((MyApplication) getActivity().getApplication()).getObjectGraph().plus(new TestNavigationModule());
og.inject(getActivity());
}
public void test_SeeSpecialText() {
onView(withId(R.id.my_text_view)).check(matches(withText(
"Special Dummy Text)));
}
@Module(includes = NavigationModule.class,
injects = { MyActivityTest.class, MyActivity.class },
overrides = true,
library = true)
static class TestNavigationModule {
@Provides
@Singleton
Navigator provideANavigator() {
return new DummyNavigator(); // that returns "Special Dummy Text"
}
}
}
Questo non funziona affatto. I miei test per il caffè funzionano, ma il TestNavigationModule è completamente ignorato ... arr ... :(
Che cosa sto sbagliando? Esiste un approccio migliore per deridere i moduli con Espresso. Ho cercato e visto esempi di Robolectric, Mockito ecc utilizzato ma voglio solo test Espresso puri e la necessità di scambiare un modulo con la mia una finta Come dovrei fare questo
EDIT:..?
così sono andato con @ user3399328 approccio di avere una definizione di elenco di moduli di test statici, verificare la presenza di null e quindi aggiungerli nella mia classe Application. Non sto ancora ottenendo la versione di Injected Test della classe, ma ho la sensazione che probabilmente è qualcosa di sbagliato con la definizione del modulo test del pugnale e non il mio ciclo di vita del caffè espresso. La ragione per cui sto facendo l'ipotesi è che aggiungo le istruzioni di debug e trovo che il modulo di test statico non è vuoto al momento dell'iniezione nella classe dell'applicazione. Potresti indicarmi la direzione di ciò che potrei eventualmente fare male. Qui ci sono frammenti di codice delle mie definizioni:
MyApplication:
@Override
public void onCreate() {
// ...
mObjectGraph = ObjectGraph.create(Modules.list(this));
// ...
}
moduli:
public class Modules {
public static List<Object> _testModules = null;
public static Object[] list(MyApplication app) {
// return new Object[]{ new AppModule(app) };
List<Object> modules = new ArrayList<Object>();
modules.add(new AppModule(app));
if (_testModules == null) {
Log.d("No test modules");
} else {
Log.d("Test modules found");
}
if (_testModules != null) {
modules.addAll(_testModules);
}
return modules.toArray();
}
}
modulo di test modificato nel mio classe di test:
@Module(overrides = true, library = true)
public static class TestNavigationModule {
@Provides
@Singleton
Navigator provideANavigator()() {
Navigator navigator = new Navigator();
navigator.setSpecialText("Dummy Text");
return navigator;
}
}
Questa è la strada da percorrere. Un modo ingegnoso per aggirare la limitazione. Oscilla signore! –
Quando provo a iniettare i miei moduli di test nel costruttore della mia sottoclasse ActivityInstrumentationTestCase2, non funziona perché l'applicazione è già stata istanziata e onCreate è già stato chiamato. Quindi non ho ancora trovato un buon metodo per dichiarare i miei moduli di test durante l'esecuzione dei test –
Attualmente sto provando la seconda soluzione di @ user3399328 (fornendo un'altra versione di ActivityInjectHelper nelle mie origini di test). Hai trovato un modo per farlo funzionare con Gradle? Continuo a correre nella classe 'Duplicate trovata negli errori del file [nome file]' che compaiono in Android Studio. – Ben