Espresso-Inaktivitätsressourcen

Eine inaktive Ressource stellt einen asynchronen Vorgang dar, dessen Ergebnisse sich auf nachfolgende Vorgänge in einem UI-Test auswirken. Wenn Sie inaktive Ressourcen bei Espresso registrieren, können Sie diese asynchronen Vorgänge beim Testen Ihrer Anwendung zuverlässiger validieren.

Erkennen, wann inaktive Ressourcen benötigt werden

Espresso bietet anspruchsvolle Synchronisierungsfunktionen. Diese Eigenschaft des Frameworks gilt jedoch nur für Vorgänge, die Nachrichten im MessageQueue posten, z. B. eine abgeleitete Klasse von View, die den Inhalt auf dem Bildschirm darstellt.

Da Espresso keine anderen asynchronen Vorgänge erkennt, einschließlich solchen, die in einem Hintergrundthread ausgeführt werden, kann Espresso in diesen Situationen keine Synchronisierungsgarantien geben. Sie müssen jeden einzelnen als inaktive Ressource registrieren, um Espresso auf die lang andauernden Vorgänge Ihrer Anwendung aufmerksam zu machen.

Wenn Sie beim Testen der Ergebnisse der asynchronen Arbeit Ihrer Anwendung keine inaktiven Ressourcen verwenden, müssen Sie möglicherweise eine der folgenden schlechten Behelfslösungen anwenden, um die Zuverlässigkeit Ihrer Tests zu verbessern:

  • Thread.sleep()-Anrufe werden hinzugefügt. Wenn du deinen Tests künstliche Verzögerungen hinzufügst, dauert die Ausführung der Testsuite länger. Außerdem können deine Tests manchmal trotzdem fehlschlagen, wenn sie auf langsameren Geräten ausgeführt werden. Außerdem lassen sich diese Verzögerungen nicht gut skalieren, da Ihre Anwendung in einem zukünftigen Release möglicherweise zeitaufwendigere asynchrone Arbeiten ausführen muss.
  • Wiederholungs-Wrapper implementieren, die in einer Schleife wiederholt prüfen, ob die Anwendung noch asynchrone Arbeiten ausführt, bis eine Zeitüberschreitung auftritt. Selbst wenn Sie in Ihren Tests eine maximale Anzahl von Wiederholungen angeben, beansprucht jede erneute Ausführung Systemressourcen, insbesondere die CPU.
  • Instanzen von CountDownLatch verwenden, die es einem oder mehreren Threads ermöglichen, zu warten, bis eine bestimmte Anzahl von Vorgängen abgeschlossen ist, die in einem anderen Thread ausgeführt werden. Für diese Objekte müssen Sie eine Zeitüberschreitung angeben. Andernfalls wird Ihre Anwendung möglicherweise auf unbestimmte Zeit blockiert. Die Latches machen Ihren Code außerdem unnötig kompliziert und erschweren die Wartung.

Mit Espresso können Sie diese unzuverlässigen Problemumgehungen aus Ihren Tests entfernen und stattdessen die asynchrone Arbeit Ihrer Anwendung als inaktive Ressourcen registrieren.

Gängige Anwendungsfälle

Wenn Sie in Ihren Tests Vorgänge wie den folgenden Beispielen ausführen, sollten Sie eine inaktive Ressource verwenden:

  • Daten aus dem Internet oder einer lokalen Datenquelle laden
  • Herstellen von Verbindungen zu Datenbanken und Callbacks
  • Dienste verwalten, entweder mithilfe eines Systemdienstes oder einer Instanz von IntentService.
  • Ausführung komplexer Geschäftslogik wie Bitmaptransformationen.

Es ist besonders wichtig, inaktive Ressourcen zu registrieren, wenn durch diese Vorgänge eine UI aktualisiert wird, die von Ihren Tests dann validiert wird.

Beispiele für inaktive Ressourcenimplementierungen

In der folgenden Liste werden mehrere Beispielimplementierungen für inaktive Ressourcen beschrieben, die Sie in Ihre App integrieren können:

CountingIdlingResource
Hält einen Zähler für aktive Aufgaben. Wenn der Zähler null ist, gilt die zugehörige Ressource als inaktiv. Diese Funktion ähnelt sehr der Funktion Semaphore. In den meisten Fällen reicht diese Implementierung aus, um die asynchrone Arbeit der Anwendung während des Tests zu verwalten.
UriIdlingResource
Ähnlich wie CountingIdlingResource, aber der Zähler muss für einen bestimmten Zeitraum null sein, bevor die Ressource als inaktiv gilt. Diese zusätzliche Wartezeit berücksichtigt aufeinanderfolgende Netzwerkanfragen, wobei eine Anwendung in Ihrem Thread direkt nach Erhalt einer Antwort auf eine vorherige Anfrage eine neue Anfrage stellen kann.
IdlingThreadPoolExecutor
Eine benutzerdefinierte Implementierung von ThreadPoolExecutor, die die Gesamtzahl der laufenden Aufgaben in den erstellten Thread-Pools verfolgt. Diese Klasse verwendet einen CountingIdlingResource, um den Zähler für aktive Aufgaben zu verwalten.
IdlingScheduledThreadPoolExecutor
Eine benutzerdefinierte Implementierung von ScheduledThreadPoolExecutor. Sie bietet dieselben Funktionen und Möglichkeiten wie die Klasse IdlingThreadPoolExecutor, kann aber auch Aufgaben erfassen, die in der Zukunft geplant sind oder deren regelmäßige Ausführung geplant ist.

Eigene inaktive Ressource erstellen

Wenn Sie in den Tests Ihrer Anwendung inaktive Ressourcen verwenden, müssen Sie möglicherweise eine benutzerdefinierte Ressourcenverwaltung oder ein benutzerdefiniertes Logging bereitstellen. In diesen Fällen reichen die im vorherigen Abschnitt aufgeführten Implementierungen möglicherweise nicht aus. In diesem Fall können Sie eine dieser inaktiven Ressourcenimplementierungen erweitern oder Ihre eigene erstellen.

Wenn Sie Ihre eigene Funktionalität für inaktive Ressourcen implementieren, beachten Sie die folgenden Best Practices, insbesondere die erste:

Ruft einen Wechsel in den Inaktivitätsstatus außerhalb von Prüfungen bei Inaktivität auf.
Wenn die Anwendung inaktiv wird, rufen Sie onTransitionToIdle() außerhalb aller Implementierungen von isIdleNow() auf. Auf diese Weise führt Espresso keine zweite, unnötige Prüfung durch, um festzustellen, ob eine bestimmte inaktive Ressource inaktiv ist.

Das folgende Code-Snippet veranschaulicht diese Empfehlung:

Kotlin

fun isIdle() {
    // DON'T call callback.onTransitionToIdle() here!
}

fun backgroundWorkDone() {
    // Background work finished.
    callback.onTransitionToIdle() // Good. Tells Espresso that the app is idle.

    // Don't do any post-processing work beyond this point. Espresso now
    // considers your app to be idle and moves on to the next test action.
}

Java

public void isIdle() {
    // DON'T call callback.onTransitionToIdle() here!
}

public void backgroundWorkDone() {
    // Background work finished.
    callback.onTransitionToIdle() // Good. Tells Espresso that the app is idle.

    // Don't do any post-processing work beyond this point. Espresso now
    // considers your app to be idle and moves on to the next test action.
}
Inaktive Ressourcen registrieren, bevor Sie sie benötigen.

Die mit inaktiven Ressourcen verbundenen Synchronisierungsvorteile werden erst nach dem ersten Aufruf der Methode isIdleNow() dieser Ressource von Espresso wirksam.

Die folgende Liste enthält mehrere Beispiele für diese Property:

  • Wenn Sie eine inaktive Ressource in einer mit @Before annotierten Methode registrieren, wird die inaktive Ressource in der ersten Zeile jedes Tests wirksam.
  • Wenn Sie in einem Test eine inaktive Ressource registrieren, wird sie bei der nächsten Espresso-basierten Aktion wirksam. Dieses Verhalten tritt auch dann auf, wenn sich die nächste Aktion im selben Test wie die Anweisung befindet, mit der die inaktive Ressource registriert wird.
Inaktive Ressourcen werden nicht mehr registriert, nachdem Sie sie verwendet haben.

Zur Schonung von Systemressourcen sollten Sie inaktive Ressourcen abmelden, sobald Sie sie nicht mehr benötigen. Wenn Sie beispielsweise eine inaktive Ressource in einer mit @Before annotierten Methode registrieren, sollte die Registrierung dieser Ressource in einer entsprechenden Methode aufgehoben werden, die mit @After annotiert ist.

Verwenden Sie eine inaktive Registry, um inaktive Ressourcen zu registrieren und ihre Registrierung aufzuheben.

Wenn Sie diesen Container für die inaktiven Ressourcen Ihrer Anwendung verwenden, können Sie inaktive Ressourcen bei Bedarf wiederholt registrieren und deren Registrierung aufheben. Das Verhalten ist weiterhin konsistent.

Nur den einfachen App-Status innerhalb inaktiver Ressourcen beibehalten.

Die inaktiven Ressourcen, die Sie implementieren und registrieren, sollten beispielsweise keine Verweise auf View-Objekte enthalten.

Inaktive Ressourcen registrieren

Espresso stellt eine Containerklasse bereit, in die Sie die inaktiven Ressourcen Ihrer Anwendung platzieren können. Diese Klasse namens IdlingRegistry ist ein eigenständiges Artefakt, das den Aufwand für Ihre Anwendung minimiert. Mit der Klasse können Sie außerdem die folgenden Schritte zur Verbesserung der Haltbarkeit Ihrer Anwendung ausführen:

  • Erstellen Sie in den Tests Ihrer App einen Verweis auf die IdlingRegistry anstelle der darin enthaltenen inaktiven Ressourcen.
  • Behalten Sie die Unterschiede in der Sammlung inaktiver Ressourcen bei, die Sie für jede Build-Variante verwenden.
  • Definieren Sie inaktive Ressourcen in den Diensten Ihrer Anwendung und nicht in den UI-Komponenten, die auf diese Dienste verweisen.

Inaktive Ressourcen in Ihre App einbinden

Auch wenn Sie inaktive Ressourcen auf verschiedene Arten zu einer Anwendung hinzufügen können, wird bei einem Ansatz die Kapselung für Ihre Anwendung beibehalten, während Sie weiterhin einen bestimmten Vorgang angeben können, den eine bestimmte inaktive Ressource darstellt.

Wenn Sie Ihrer Anwendung inaktive Ressourcen hinzufügen, empfehlen wir dringend, die Logik für inaktive Ressourcen in der Anwendung selbst zu platzieren und in Ihren Tests nur die Registrierungs- und Registrierungsvorgänge auszuführen.

Obwohl Sie mit diesem Ansatz in der ungewöhnlichen Situation eine reine Testoberfläche im Produktionscode verwenden, können Sie inaktive Ressourcen mit bereits vorhandenem Code umschließen und dabei die APK-Größe und die Anzahl der Methoden Ihrer App beibehalten.

Alternative Ansätze

Wenn Sie im Produktionscode Ihrer Anwendung keine Logik für inaktive Ressourcen verwenden möchten, gibt es mehrere andere praktikable Integrationsstrategien:

  • Erstellen Sie Build-Varianten wie die Produktvarianten von Gradle und verwenden Sie inaktive Ressourcen nur im Debug-Build Ihrer App.
  • Verwenden Sie ein Abhängigkeitsinjektions-Framework wie Dagger, um das Diagramm der inaktive Ressourcenabhängigkeit Ihrer App in Ihre Tests einzufügen. Wenn du Dagger 2 verwendest, sollte die Injektion von einer Unterkomponente stammen.
  • Implementieren Sie eine inaktive Ressource in den Tests Ihrer Anwendung und geben Sie den Teil der Anwendungsimplementierung frei, der in diesen Tests synchronisiert werden muss.

    Achtung : Obwohl diese Designentscheidung einen eigenständigen Verweis auf inaktive Ressourcen zu erzeugen scheint, wird dadurch auch die Kapselung bei allen außer den einfachsten Anwendungen unterbrochen.

Weitere Informationen

Weitere Informationen zur Verwendung von Espresso in Android-Tests finden Sie in den folgenden Ressourcen.

Produktproben