Ist dies eine korrekte Methode, um Dagger 2 für Android im Komponententest zu verwenden, um Abhängigkeiten mit Mocks / Fakes zu überschreiben?

Für 'reguläre' Java-Projekte ist es einfach, die Abhängigkeiten in den Unit-Tests mit Schein- / Schein-Abhängigkeiten zu überschreiben. Sie müssen einfach Ihr @ bauDolc -Komponente und geben Sie es an die 'Haupt'-Klasse, die Ihre Anwendung antreibt.

Für Androi Dinge sindnicht so einfach und ich habe lange nach anständigen Beispielen gesucht, aber ich konnte sie nicht finden, also musste ich meine eigene Implementierung erstellen und ich würde mich sehr über Feedback freuen Weg, um die Abhängigkeiten zu überschreiben.

Hier die Erklärung Projektquelle finden Sie auf github):

Da wir eine einfache App haben, die Dolch 2 mit einer Dolchkomponente mit einem Modul verwendet, möchten wir Android-Komponententests erstellen, die JUnit4 verwenden, Mockito undEspress:

In demMyApp Application Klasse Die Komponente / der Injektor wird folgendermaßen initialisiert:

public class MyApp extends Application {
    private MyDaggerComponent mInjector;

    public void onCreate() {
        super.onCreate();
        initInjector();
    }

    protected void initInjector() {
        mInjector = DaggerMyDaggerComponent.builder().httpModule(new HttpModule(new OkHttpClient())).build();

        onInjectorInitialized(mInjector);
    }

    private void onInjectorInitialized(MyDaggerComponent inj) {
        inj.inject(this);
    }

    public void externalInjectorInitialization(MyDaggerComponent injector) {
        mInjector = injector;

        onInjectorInitialized(injector);
    }

    ...

In dem obigen Code: Normaler Anwendungsstart geht durchonCreate() welches ruftinitInjector() erstellt den Injektor und ruft dann @ aonInjectorInitialized().

DasexternalInjectorInitialization() -Methode ist bei den Unit-Tests aufzurufen, umset der Injektor von einer externen Quelle, d. h. ein Komponententest.

So weit, ist es gut

Mal sehen, wie die Dinge auf der Unit-Testseite aussehen:

Wir müssen MyTestApp-Aufrufe erstellen, die die MyApp-Klasse erweitern und @ überschreibeinitInjector mit leerer Methode, um die Erstellung von Doppelinjektoren zu vermeiden (da wir in unserem Komponententest eine neue erstellen):

public class MyTestApp extends MyApp {
    @Override
    protected void initInjector() {
        // empty
    }
}

Dann müssen wir die ursprüngliche MyApp irgendwie durch MyTestApp ersetzen. Dies erfolgt über einen benutzerdefinierten Testläufer:

public class MyTestRunner extends AndroidJUnitRunner {
    @Override
    public Application newApplication(ClassLoader cl,
                                      String className,
                                      Context context) throws InstantiationException,
            IllegalAccessException,
            ClassNotFoundException {


        return super.newApplication(cl, MyTestApp.class.getName(), context);
    }
}

... wo innewApplication() Wir ersetzen die ursprüngliche App-Klasse durch die Testklasse.

Dann müssen wir dem Test-Framework mitteilen, dass wir unseren benutzerdefinierten Test-Runner verwenden möchten, und dies in dem build.gradle tun, das wir hinzufügen:

defaultConfig {
    ...
    testInstrumentationRunner 'com.bolyartech.d2overrides.utils.MyTestRunner'
    ...
}

Wenn ein Komponententest durchgeführt wird, wird unser OriginalMyApp wird durch @ ersetMyTestApp. Jetzt müssen wir unsere Komponente / unseren Injektor mit Mocks / Fakes erstellen und der App mit @ verseheexternalInjectorInitialization(). Zu diesem Zweck erweitern wir die normale ActivityTestRule:

@Rule
public ActivityTestRule<Act_Main> mActivityRule = new ActivityTestRule<Act_Main>(
        Act_Main.class) {


    @Override
    protected void beforeActivityLaunched() {
        super.beforeActivityLaunched();

        OkHttpClient mockHttp = create mock OkHttpClient

        MyDaggerComponent injector = DaggerMyDaggerComponent.
                builder().httpModule(new HttpModule(mockHttp)).build();

        MyApp app = (MyApp) InstrumentationRegistry.getInstrumentation().
                getTargetContext().getApplicationContext();

        app.externalInjectorInitialization(injector);

    }
};

und dann machen wir unseren Test wie gewohnt:

@Test
public void testHttpRequest() throws IOException {
    onView(withId(R.id.btn_execute)).perform(click());

    onView(withId(R.id.tv_result))
            .check(matches(withText(EXPECTED_RESPONSE_BODY)));
}

ie @Above-Methode für (Modul-) Überschreibungen funktioniert, erfordert jedoch die Erstellung einer Testklasse pro Test, damit für jeden Test eine separate Regel / (Verspottungs-Setup) angegeben werden kann. Ich vermute / rate / hoffe, dass es einen einfacheren und eleganteren Weg gibt. Gibt es

Diese Methode basiert größtenteils auf der Antwort von @tomrozb fürdiese Frag. Ich habe gerade die Logik hinzugefügt, um die Erzeugung von Doppelinjektoren zu vermeiden.

Antworten auf die Frage(4)

Ihre Antwort auf die Frage