Grundlagen zum Testen von Android-Apps

Auf dieser Seite werden die wichtigsten Grundsätze des Testens von Android-Apps beschrieben, einschließlich der zentralen Best Practices und ihrer Vorteile.

Vorteile von Tests

Tests sind ein wesentlicher Bestandteil der App-Entwicklung. Wenn Sie Ihre App regelmäßig testen, können Sie vor der Veröffentlichung die Richtigkeit, das funktionale Verhalten und die Nutzerfreundlichkeit Ihrer App überprüfen.

Sie können Ihre App manuell testen, indem Sie die App aufrufen. Sie können verschiedene Geräte und Emulatoren verwenden, die Systemsprache ändern und versuchen, jeden Nutzerfehler zu generieren oder jeden Nutzerfluss zu durchlaufen.

Manuelle Tests lassen sich jedoch schlecht skalieren. Außerdem lassen sich Regressionen im Verhalten Ihrer Anwendung leicht übersehen. Bei automatisierten Tests werden Tools verwendet, die Tests für Sie durchführen. Sie sind schneller, besser wiederholbar und geben Ihnen in der Regel schon früher im Entwicklungsprozess umsetzbares Feedback zu Ihrer App.

Testtypen in Android

Mobile Anwendungen sind komplex und müssen in vielen Umgebungen gut funktionieren. Daher gibt es viele Arten von Tests.

Betreff

Je nach Subjekt gibt es beispielsweise verschiedene Arten von Tests:

  • Funktionstests: Funktioniert meine App wie vorgesehen?
  • Leistungstests: Lassen sich die Tests schnell und effizient durchführen?
  • Test zur Barrierefreiheit: Funktioniert das Tool gut mit Bedienungshilfen?
  • Kompatibilitätstest: Funktioniert das Tool auf jedem Gerät und auf jedem API-Level?

Aufgabenstellung

Tests variieren auch je nach Größe oder Isolationsgrad:

  • Unittests oder kleine Tests prüfen nur einen sehr kleinen Teil der App, z. B. eine Methode oder Klasse.
  • Bei End-to-End-Tests oder großen Tests werden größere Teile der App gleichzeitig überprüft, z. B. ein ganzer Bildschirm oder ein Nutzerfluss.
  • Mittlere Tests befinden sich dazwischen und prüfen die Integration zwischen zwei oder mehr Einheiten.
Tests können entweder klein, mittel oder groß sein.
Abbildung 1: Testbereiche in einer typischen Anwendung.

Es gibt viele Möglichkeiten, Tests zu klassifizieren. Der wichtigste Unterschied für App-Entwickler ist jedoch, wo Tests ausgeführt werden.

Instrumentierte und lokale Tests im Vergleich

Sie können Tests auf einem Android-Gerät oder auf einem anderen Computer ausführen:

  • Instrumentierte Tests werden auf einem Android-Gerät ausgeführt, entweder physisch oder emuliert. Die App wird zusammen mit einer Test-App erstellt und installiert, die Befehle einschleust und den Status liest. Instrumentierte Tests sind in der Regel UI-Tests, bei denen eine App gestartet und dann mit ihr interagiert wird.
  • Lokale Tests werden auf Ihrem Entwicklungscomputer oder einem Server ausgeführt, daher werden sie auch als Host-Tests bezeichnet. Sie sind meist klein und schnell, wodurch das Testobjekt vom Rest der App isoliert wird.
Tests können als instrumentierte Tests auf einem Gerät oder als lokale Tests auf Ihrem Entwicklungscomputer ausgeführt werden.
Abbildung 2: Verschiedene Arten von Tests, je nachdem, wo sie ausgeführt werden.

Nicht alle Einheitentests sind lokal und nicht alle End-to-End-Tests werden auf einem Gerät ausgeführt. Beispiel:

  • Großer lokaler Test: Sie können einen lokal ausgeführten Android-Simulator verwenden, z. B. Robolectric.
  • Kleiner instrumentierter Test: Sie können prüfen, ob Ihr Code mit einer Framework-Funktion wie einer SQLite-Datenbank gut funktioniert. Sie können diesen Test auf mehreren Geräten ausführen, um die Integration mit mehreren SQLite-Versionen zu prüfen.

Beispiele

Die folgenden Snippets zeigen, wie Sie in einem instrumentierten UI-Test mit der UI interagieren, bei dem auf ein Element geklickt und geprüft wird, ob ein anderes Element angezeigt wird.

Espresso

// When the Continue button is clicked
onView(withText("Continue"))
    .perform(click())

// Then the Welcome screen is displayed
onView(withText("Welcome"))
    .check(matches(isDisplayed()))

Benutzeroberfläche zum Verfassen

// When the Continue button is clicked
composeTestRule.onNodeWithText("Continue").performClick()

// Then the Welcome screen is displayed
composeTestRule.onNodeWithText("Welcome").assertIsDisplayed()

Dieses Snippet zeigt einen Teil eines Einheitentests für ein ViewModel (lokaler, hostseitiger Test):

// Given an instance of MyViewModel
val viewModel = MyViewModel(myFakeDataRepository)

// When data is loaded
viewModel.loadData()

// Then it should be exposing data
assertTrue(viewModel.data != null)

Teststrategie definieren

Im Idealfall würden Sie jede Codezeile in Ihrer App auf jedem Gerät testen, mit dem Ihre App kompatibel ist. Leider ist dieser Ansatz zu langsam und zu teuer, um praktikabel zu sein.

Eine gute Teststrategie findet ein angemessenes Gleichgewicht zwischen der Genauigkeit eines Tests, seiner Geschwindigkeit und seiner Zuverlässigkeit. Die Ähnlichkeit der Testumgebung mit einem echten Gerät bestimmt die Genauigkeit des Tests. High-Fidelity-Tests werden auf emulierten Geräten oder auf dem physischen Gerät selbst ausgeführt. Tests mit niedrigerer Qualität können auf der JVM Ihrer lokalen Workstation ausgeführt werden. High-Fidelity-Tests sind oft langsamer und erfordern mehr Ressourcen. Daher sollte nicht jeder Test ein High-Fidelity-Test sein.

Instabile Tests

Fehler treten auch bei korrekt konzipierten und implementierten Testläufen auf. Wenn beispielsweise ein Test auf einem echten Gerät ausgeführt wird, kann ein automatisches Update mitten in einem Test beginnen und fehlschlagen. Subtile Race-Bedingungen in Ihrem Code treten möglicherweise nur in einem kleinen Prozentsatz der Zeit auf. Tests, die nicht zu 100% bestanden werden, sind unzuverlässig.

Testbare Architektur

Bei einer testbaren Anwendungsarchitektur folgt der Code einer Struktur, mit der Sie verschiedene Teile leicht isoliert testen können. Testbare Architekturen haben weitere Vorteile, z. B. bessere Lesbarkeit, Verwaltbarkeit, Skalierbarkeit und Wiederverwendbarkeit.

Eine Architektur, die nicht testbar ist, liefert Folgendes:

  • Größere, langsamere und instabilere Tests. Klassen, für die keine Einheitentests möglich sind, müssen möglicherweise durch größere Integrations- oder UI-Tests abgedeckt werden.
  • Weniger Möglichkeiten zum Testen verschiedener Szenarien. Größere Tests sind langsamer, sodass das Testen aller möglichen Status einer App unrealistisch sein kann.

Weitere Informationen zu den Architekturrichtlinien finden Sie im Leitfaden zur Anwendungsarchitektur.

Ansätze zur Entkopplung

Wenn Sie einen Teil einer Funktion, einer Klasse oder eines Moduls aus dem Rest extrahieren können, ist das Testen einfacher und effektiver. Diese Vorgehensweise wird als Entkopplung bezeichnet und ist das wichtigste Konzept einer testbaren Architektur.

Zu den gängigen Entkopplungstechniken gehören:

  • Apps in Ebenen wie „Präsentation“, „Domain“ und „Daten“ aufteilen Sie können eine App auch in Module aufteilen (ein Modul pro Feature).
  • Fügen Sie Entitäten mit großen Abhängigkeiten wie Aktivitäten und Fragmenten keine Logik hinzu. Sie können diese Klassen als Einstiegspunkte in das Framework verwenden und UI- und Geschäftslogik an einen anderen Ort verschieben, z. B. in eine zusammensetzbare Funktion, eine ViewModel- oder Domainebene.
  • Vermeiden Sie direkte Framework-Abhängigkeiten in Klassen, die Geschäftslogik enthalten. Verwenden Sie beispielsweise keine Android-Kontexte in ViewModels.
  • Machen Sie das Ersetzen von Abhängigkeiten einfach. Verwenden Sie beispielsweise Schnittstellen anstelle von konkreten Implementierungen. Verwenden Sie die Abhängigkeitsinjektion, auch wenn Sie kein DI-Framework verwenden.

Nächste Schritte

Jetzt, da Sie wissen, warum Sie testen sollten und die zwei Haupttypen von Tests sind, können Sie unter Was zu testen ist nachlesen.

Wenn Sie alternativ Ihren ersten Test erstellen und durch Ausprobieren lernen möchten, rufen Sie die Testcodelabs auf.