Grundlagen testen

Dieses Codelab ist Teil des Kurses „Advanced Android in Kotlin“. Sie profitieren von diesem Kurs, wenn Sie die Codelabs nacheinander durcharbeiten. Das ist aber nicht zwingend erforderlich. Alle Kurs-Codelabs finden Sie auf der Landingpage für Codelabs auf Android-Geräten für Fortgeschrittene.

Einführung

Wenn Sie die erste Funktion Ihrer ersten App implementiert haben, haben Sie wahrscheinlich den Code ausgeführt, um zu prüfen, ob sie wie erwartet funktioniert. Sie haben einen Test durchgeführt, aber einen manuellen Test. Da Sie ständig neue Funktionen hinzugefügt und aktualisiert haben, sollten Sie Ihren Code mit großer Wahrscheinlichkeit weiter testen. Aber manuell zu arbeiten, ist müde, fehleranfällig und lässt sich nicht skalieren.

Computer eignen sich hervorragend für die Skalierung und Automatisierung. Entwickler in großen und kleinen Unternehmen schreiben deshalb automatisierte Tests. Das sind Softwaretests, die von Ihnen nicht manuell durchgeführt werden müssen.

In dieser Codelabs-Reihe lernen Sie, wie Sie eine Sammlung von Tests, eine sogenannte Test-Suite, für eine reale App erstellen.

In diesem ersten Codelab werden die Grundlagen von Android-Tests beschrieben. Die ersten Tests werden durchgeführt und du erfährst, wie du LiveData und ViewModel testest.

Was Sie bereits wissen sollten

Sie sollten mit Folgendem vertraut sein:

Lerninhalte

Sie lernen die folgenden Themen kennen:

  • Unittests für Android schreiben und ausführen
  • Testgetriebene Entwicklung verwenden
  • Instrumentierte Tests und lokale Tests auswählen

Sie lernen die folgenden Bibliotheken und Codekonzepte kennen:

Aufgaben

  • Lokale, instrumentierte Tests in Android einrichten, ausführen und interpretieren
  • Unittests in Android mit JUnit4 und Hamcrest durchführen
  • Einfache LiveData- und ViewModel-Tests schreiben.

In dieser Codelab-Reihe arbeiten Sie mit der To-do-Notes-App. Sie können damit Aufgaben erledigen und in einer Liste anzeigen lassen. Diese können Sie dann als abgeschlossen markieren, sie filtern oder löschen.

Diese App wurde in Kotlin geschrieben, hat mehrere Bildschirme, verwendet Jetpack-Komponenten und folgt der Architektur aus einem Leitfaden zur App-Architektur. Wenn du lernst, wie du diese App testest, kannst du Apps testen, die dieselben Bibliotheken und Architekturen verwenden.

Lade den Code herunter, um loszulegen:

Zip herunterladen

Alternativ können Sie das GitHub-Repository für den Code klonen:

$ git clone https://github.com/googlecodelabs/android-testing.git
$ cd android-testing
$ git checkout starter_code

In dieser Aufgabe führen Sie die App aus und sehen sich die Codebasis an.

Schritt 1: Beispielanwendung ausführen

Nachdem du die TO-DO-App heruntergeladen hast, öffne sie in Android Studio und führe sie aus. Es sollte kompiliert werden. Gehen Sie so vor, um die Anwendung zu entdecken:

  • Erstellen Sie eine neue Aufgabe über die Plus-Aktionsschaltfläche. Geben Sie zuerst einen Titel und dann weitere Informationen zur Aufgabe ein. Speichern Sie den Text mit dem grünen Häkchen.
  • Klicken Sie in der Liste der Aufgaben auf den Titel der gerade erledigten Aufgabe und sehen Sie sich die Detailseite für die Aufgabe an, um den Rest der Beschreibung aufzurufen.
  • Wenn Sie den Status in der Liste oder auf dem Detailbildschirm auf Abgeschlossen setzen möchten, klicken Sie das zugehörige Kästchen an.
  • Kehren Sie zum Bildschirm mit den Aufgaben zurück, öffnen Sie das Filtermenü und filtern Sie die Aufgaben nach Aktiv und Erledigt.
  • Öffnen Sie die Navigationsleiste und klicken Sie auf Statistiken.
  • Kehren Sie zur Übersichtsseite zurück und wählen Sie im Navigationsmenü die Option Alle erledigten Aufgaben löschen aus, um alle Aufgaben mit dem Status Erledigt zu löschen.

Schritt 2: Beispielcode für die App ansehen

Die TO-DO-App basiert auf dem beliebten Test der Architektur und den Architektur-Blueprints, die auf der reaktiven Architektur basieren. Die Anwendung richtet sich nach der Architektur aus dem Leitfaden zur App-Architektur. Dabei werden ViewModels mit Fragmenten, einem Repository und Room verwendet. Wenn Sie mit einem der folgenden Beispiele vertraut sind, sieht die App ähnlich aus:

Es ist wichtiger, dass Sie die allgemeine Architektur der App verstehen, als ein besseres Verständnis der Logik auf einer einzelnen Ebene zu haben.

Hier findest du eine Zusammenfassung der Pakete:

Paket: com.example.android.architecture.blueprints.todoapp

.addedittask

Aufgabenbildschirm hinzufügen oder bearbeiten:UI-Ebenencode zum Hinzufügen oder Bearbeiten einer Aufgabe.

.data

Datenschicht: Dies ist die Datenschicht der Aufgaben. Es enthält den Datenbank-, Netzwerk- und Repository-Code.

.statistics

Statistikbildschirm: UI-Ebenencode für den Statistikbildschirm

.taskdetail

Bildschirm mit den Aufgabendetails:UI-Ebenencode für eine einzelne Aufgabe.

.tasks

Auf dem Bildschirm „Aufgaben“: Code der UI-Ebene für die Liste aller Aufgaben.

.util

Dienstprogramme:Gemeinsame Klassen, die in verschiedenen Teilen der App verwendet werden, z.B. für das Wischen-Layout auf mehreren Bildschirmen.

Datenschicht (.data)

Diese App enthält eine simulierte Netzwerkebene im Paket remote und eine Datenbankschicht im Paket local. Der Einfachheit halber wird die Netzwerkebene in diesem Projekt mit nur einem HashMap mit einer Verzögerung simuliert, anstatt echte Netzwerkanfragen zu stellen.

Die DefaultTasksRepository-Koordinaten oder -Koordinaten zwischen der Netzwerk- und der Datenbankebene sind die Daten, die an die UI-Ebene zurückgegeben werden.

UI-Ebene ( .addedittask, .stats, .taskdetail, .tasks)

Jedes UI-Ebenenpaket enthält ein Fragment und ein Ansichtsmodell sowie alle anderen Klassen, die für die UI erforderlich sind, z. B. einen Adapter für die Aufgabenliste. Die TaskActivity ist die Aktivität, die alle Fragmente enthält.

Navigation

Die Navigation für die App wird über die Navigationskomponente gesteuert. Die Definition ist in der Datei nav_graph.xml definiert. Die Navigation wird in den Aufrufmodellen mithilfe der Klasse Event ausgelöst. Die Aufrufmodelle bestimmen auch, welche Argumente übergeben werden. Die Fragmente beobachten die Event und führen die tatsächliche Navigation zwischen den Bildschirmen durch.

In dieser Aufgabe führen Sie Ihre ersten Tests durch.

  1. Öffnen Sie in Android Studio den Bereich Projekt und suchen Sie die folgenden drei Ordner:
  • com.example.android.architecture.blueprints.todoapp
  • com.example.android.architecture.blueprints.todoapp (androidTest)
  • com.example.android.architecture.blueprints.todoapp (test)

Diese Ordner werden als Quellsätze bezeichnet. Quellsätze sind Ordner, die Quellcode für Ihre App enthalten. Die Quellsätze, die in Grün dargestellt sind (androidTest und test), enthalten Ihre Tests. Wenn Sie ein neues Android-Projekt erstellen, sehen Sie standardmäßig die folgenden drei Quellsätze. Sie sind:

  • main: Enthält Ihren App-Code. Dieser Code wird von allen Versionen der App verwendet, die Sie erstellen können (als Build-Varianten bezeichnet).
  • androidTest: Enthält Tests, die als instrumentierte Tests bezeichnet werden.
  • test: Enthält Tests, die als lokale Tests bezeichnet werden.

Der Unterschied zwischen lokalen Tests und instrumentierten Tests besteht darin, wie sie ausgeführt werden.

Lokale Tests (test Quelle festgelegt)

Diese Tests werden lokal auf Ihrem JVM ausgeführt und benötigen keinen Emulator oder ein physisches Gerät. Das bedeutet, dass sie schnell laufen, aber ihre Genauigkeit niedriger ist. Das heißt, dass sie weniger wie in der realen Welt agieren.

In Android Studio werden lokale Tests durch ein grünes und rotes Dreiecksymbol dargestellt.

Instrumentierte Tests (androidTest Quell-Set)

Diese Tests werden auf echten oder emulierten Android-Geräten ausgeführt, sodass Sie sehen können, was in der Praxis passiert. Die Tests sind aber wesentlich langsamer.

In Android Studio werden instrumentierte Tests durch ein Android mit einem grünen und roten Dreiecksymbol dargestellt.

Schritt 1: Lokalen Test ausführen

  1. Öffnen Sie den Ordner test, bis Sie die Datei ExampleUnitTest.kt finden.
  2. Klicken Sie mit der rechten Maustaste darauf und wählen Sie RunExampleUnitTest aus.

Unten auf dem Bildschirm sollte im Fenster Ausführen die folgende Ausgabe zu sehen sein:

  1. Beachten Sie die grünen Häkchen und maximieren Sie die Testergebnisse, um zu bestätigen, dass der Test addition_isCorrect bestanden wurde. Es ist erfreulich, dass die Ergänzung wie erwartet funktioniert.

Schritt 2: Test fehlschlagen

Unten sehen Sie den Test, den Sie gerade ausgeführt haben.

BeispielUnitTest.kt

// A test class is just a normal class
class ExampleUnitTest {

   // Each test is annotated with @Test (this is a Junit annotation)
   @Test
   fun addition_isCorrect() {
       // Here you are checking that 4 is the same as 2+2
       assertEquals(4, 2 + 2)
   }
}

Beachten Sie, dass bei Tests

  • sind eine Klasse in einem der Test-Quellsätze.
  • Sie enthalten Funktionen, die mit der Annotation @Test beginnen. Jede Funktion ist ein einzelner Test.
  • u8suual enthalten Assertion-Erklärungen.

Android verwendet die Testbibliothek JUnit zum Testen mit diesem Codelab. Sowohl Assertions als auch die @Test-Annotation stammen aus JUnit.

Eine Assektion ist der Kern Ihres Tests. Dies ist eine Code-Anweisung, mit der geprüft wird, ob sich Ihr Code oder Ihre App wie erwartet verhält. In diesem Fall lautet die Assertion assertEquals(4, 2 + 2), sodass 4 gleich 2 + 2 ist.

Wenn Sie sehen möchten, wie ein fehlgeschlagener Test aussieht, fügen Sie eine Assertion hinzu, die leicht zu erkennen ist. Es wird geprüft, ob 3 1 + 1 entspricht.

  1. Fügen Sie assertEquals(3, 1 + 1) zum addition_isCorrect-Test hinzu.

BeispielUnitTest.kt

class ExampleUnitTest {

   // Each test is annotated with @Test (this is a Junit annotation)
   @Test
   fun addition_isCorrect() {
       assertEquals(4, 2 + 2)
       assertEquals(3, 1 + 1) // This should fail
   }
}
  1. Test ausführen
  1. Neben den Testergebnissen ist ein „X“ zu sehen.

  1. Weitere Hinweise:
  • Eine einzelne fehlgeschlagene Assertion schlägt den gesamten Test fehl.
  • Ihnen wird der erwartete Wert (3) gegenüber dem tatsächlich berechneten Wert (2) mitgeteilt.
  • Du wirst zur Zeile der fehlerhaften Assertion (ExampleUnitTest.kt:16) weitergeleitet.

Schritt 3: Instrumentierte Tests durchführen

Instrumentierte Tests befinden sich in der androidTest-Quelle.

  1. Öffnen Sie den Quellsatz androidTest.
  2. Führen Sie den Test „ExampleInstrumentedTest“ aus.

BeispielInstrumentedTest

@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
    @Test
    fun useAppContext() {
        // Context of the app under test.
        val appContext = InstrumentationRegistry.getInstrumentation().targetContext
        assertEquals("com.example.android.architecture.blueprints.reactive",
            appContext.packageName)
    }
}

Im Gegensatz zum lokalen Test wird der Test auf einem Gerät durchgeführt – im Beispiel unter einem emulierten Pixel 2:

Wenn ein Gerät angehängt ist oder ein Emulator läuft, sollte der Test im Emulator ausgeführt werden.

In dieser Aufgabe schreiben Sie Tests für getActiveAndCompleteStats. Damit wird der Prozentsatz aktiver und vollständiger Aufgabenstatistiken für Ihre App berechnet. Diese Werte finden Sie auf dem Statistikbildschirm der App.

Schritt 1: Testklasse erstellen

  1. Öffnen Sie in der main-Quelle in todoapp.statistics StatisticsUtils.kt.
  2. Suchen Sie die Funktion getActiveAndCompletedStats.

StatisticsUtils.kt

internal fun getActiveAndCompletedStats(tasks: List<Task>?): StatsResult {

   val totalTasks = tasks!!.size
   val numberOfActiveTasks = tasks.count { it.isActive }
   val activePercent = 100 * numberOfActiveTasks / totalTasks
   val completePercent = 100 * (totalTasks - numberOfActiveTasks) / totalTasks

   return StatsResult(
       activeTasksPercent = activePercent.toFloat(),
       completedTasksPercent = completePercent.toFloat()
   )
  
}

data class StatsResult(val activeTasksPercent: Float, val completedTasksPercent: Float)

Die Funktion getActiveAndCompletedStats akzeptiert eine Liste von Aufgaben und gibt eine StatsResult zurück. StatsResult ist eine Datenklasse, die zwei Zahlen enthält: den Prozentsatz der erledigten Aufgaben und den Prozentsatz, der aktiv ist.

Android Studio bietet dir Tools zum Generieren von Test-Stubs, mit denen du die Tests für diese Funktion implementieren kannst.

  1. Klicken Sie mit der rechten Maustaste auf getActiveAndCompletedStats und wählen Sie Erstellen &Testen aus.

Das Dialogfeld Test erstellen wird geöffnet:

  1. Ändern Sie den Kursnamen in StatisticsUtilsTest (anstatt StatisticsUtilsKtTest; es ist etwas schöner, wenn KT im Namen der Testklasse nicht verwendet wird).
  2. Alle anderen Standardeinstellungen beibehalten. JUnit 4 ist die passende Testbibliothek. Das Zielpaket ist korrekt und gibt den Speicherort der Klasse „StatisticsUtils“ wieder. Sie müssen kein Kästchen anklicken, um zusätzlichen Code zu generieren. Allerdings erstellen Sie den Test von Grund auf neu.
  3. Drücken Sie OK.

Das Dialogfeld Destination Directory auswählen wird geöffnet:

Sie führen einen lokalen Test durch, da Ihre Funktion mathematische Berechnungen durchführt und keinen Android-spezifischen Code enthält. Sie müssen es also nicht auf einem echten oder emulierten Gerät ausführen.

  1. Wählen Sie das test-Verzeichnis aus (nicht androidTest), da Sie lokale Tests schreiben.
  2. Klicken Sie auf OK.
  3. Sie sehen, dass die Klasse StatisticsUtilsTest in test/statistics/ generiert wurde.

Schritt 2: Erste Testfunktion schreiben

Sie schreiben einen Test, bei dem Folgendes geprüft wird:

  • Wenn es keine abgeschlossenen Aufgaben und eine aktive Aufgabe gibt,
  • liegt der Prozentsatz der aktiven Tests bei 100 %.
  • und der Prozentsatz der erledigten Aufgaben ist 0%.
  1. Öffnen Sie StatisticsUtilsTest.
  2. Erstellen Sie eine Funktion namens „getActiveAndCompletedStats_noCompleted_returnsHundredZero“.

StatisticsUtilsTest.kt

class StatisticsUtilsTest {

    fun getActiveAndCompletedStats_noCompleted_returnsHundredZero() {
        // Create an active task

        // Call your function

        // Check the result
    }
}
  1. Fügen Sie oberhalb des Funktionsnamens die Annotation @Test hinzu, um sie als Test zu kennzeichnen.
  2. Eine Liste mit Aufgaben erstellen
// Create an active task 
val tasks = listOf<Task>(
            Task("title", "desc", isCompleted = false)
        )
  1. Rufen Sie getActiveAndCompletedStats mit diesen Aufgaben auf.
// Call your function
val result = getActiveAndCompletedStats(tasks)
  1. Prüfen Sie mit Assertions, ob result wie erwartet funktioniert.
// Check the result
assertEquals(result.completedTasksPercent, 0f)
assertEquals(result.activeTasksPercent, 100f)

Hier ist der vollständige Code.

StatisticsUtilsTest.kt

class StatisticsUtilsTest {

    @Test
    fun getActiveAndCompletedStats_noCompleted_returnsHundredZero() {

        // Create an active task (the false makes this active)
        val tasks = listOf<Task>(
            Task("title", "desc", isCompleted = false)
        )
        // Call your function
        val result = getActiveAndCompletedStats(tasks)

        // Check the result
        assertEquals(result.completedTasksPercent, 0f)
        assertEquals(result.activeTasksPercent, 100f)
    }
}
  1. Führen Sie den Test aus. Klicken Sie mit der rechten Maustaste auf StatisticsUtilsTest und wählen Sie Ausführen aus.

Sie sollte Folgendes weitergeben:

Schritt 3: Hamcrest-Abhängigkeit hinzufügen

Da deine Tests der Dokumentation des Codes dienen, sind sie für Menschen lesbar. Vergleichen Sie die folgenden zwei Assertions:

assertEquals(result.completedTasksPercent, 0f)

// versus

assertThat(result.completedTasksPercent, `is`(0f))

Die zweite Behauptung ist mehr wie ein menschlicher Satz. Sie ist in einem Assertion-Framework namens Hamcrest geschrieben. Ein weiteres gutes Tool zum Schreiben lesbarer Assertions ist die Truth-Bibliothek. In diesem Codelab verwendest du Hamcrest, um Assertions zu schreiben.

  1. Öffnen Sie build.grade (Module: app) und fügen Sie die folgende Abhängigkeit hinzu.

app/build.gradle

dependencies {
    // Other dependencies
    testImplementation "org.hamcrest:hamcrest-all:$hamcrestVersion"
}

In der Regel verwenden Sie implementation, wenn Sie eine Abhängigkeit hinzufügen. Hier verwenden Sie testImplementation. Wenn Sie Ihre App mit der Welt teilen möchten, sollten Sie die Größe Ihres APKs nicht mit dem Testcode oder den Abhängigkeiten in Ihrer App überlasten. Sie können festlegen, ob eine Bibliothek im Haupt- oder Testcode enthalten sein soll, indem Sie Gradle-Konfigurationen verwenden. Die am häufigsten verwendeten Konfigurationen sind:

  • implementation: Die Abhängigkeit ist in allen Quellsätzen, einschließlich der Testquellensätze, verfügbar.
  • testImplementation: Die Abhängigkeit ist nur im Test-Quellsatz verfügbar.
  • androidTestImplementation: Die Abhängigkeit ist nur im androidTest-Quellsatz verfügbar.

Welche Konfiguration verwendet wird, definiert, wo die Abhängigkeit verwendet werden kann. Schreiben Sie:

testImplementation "org.hamcrest:hamcrest-all:$hamcrestVersion"

Das bedeutet, dass Hamcrest nur in der Testquelle verfügbar ist. Außerdem wird Hamcrest nicht in die endgültige App aufgenommen.

Schritt 4: Hamcrest zum Schreiben von Assertions verwenden

  1. Aktualisieren Sie den getActiveAndCompletedStats_noCompleted_returnsHundredZero()-Test, um Hamcrest assertThat anstelle von assertEquals zu verwenden.
// REPLACE
assertEquals(result.completedTasksPercent, 0f)
assertEquals(result.activeTasksPercent, 100f)

// WITH
assertThat(result.activeTasksPercent, `is`(100f))
assertThat(result.completedTasksPercent, `is`(0f))

Beachten Sie, dass Sie den Import import org.hamcrest.Matchers.`is` verwenden können, wenn Sie dazu aufgefordert werden.

Der letzte Test sieht wie der unten angegebene Code aus.

StatisticsUtilsTest.kt

import com.example.android.architecture.blueprints.todoapp.data.Task
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.`is`
import org.junit.Test

class StatisticsUtilsTest {

    @Test
    fun getActiveAndCompletedStats_noCompleted_returnsHundredZero {

        // Create an active tasks (the false makes this active)
        val tasks = listOf<Task>(
            Task("title", "desc", isCompleted = false)
        )
        // Call your function
        val result = getActiveAndCompletedStats(tasks)

        // Check the result
        assertThat(result.activeTasksPercent, `is`(100f))
        assertThat(result.completedTasksPercent, `is`(0f))

    }
}
  1. Testen Sie den aktualisierten Test, um sicherzustellen, dass er weiterhin funktioniert.

In diesem Codelab lernen Sie nicht alles über Hamcrest. Wenn Sie mehr erfahren möchten, sehen Sie sich diese offizielle Anleitung an.

Dies ist eine optionale Aufgabe für die Praxis.

In dieser Aufgabe schreiben Sie mit JUnit und Hamcrest weitere Tests. Sie schreiben auch Tests mit einer Strategie, die aus der Programmübung von Test Driven Development abgeleitet wurde. Test Driven Development (TDD) ist eine Schule von Programmieren, die besagt, dass Sie statt des Featurecodes zuerst die Tests schreiben. Schreiben Sie dann den Featurecode, um Ihre Tests zu bestehen.

Schritt 1: Tests schreiben

Schreiben Sie Tests, wenn Sie eine normale Aufgabenliste haben:

  1. Wenn es eine abgeschlossene Aufgabe und keine aktiven Aufgaben gibt, sollte der Prozentsatz für activeTasks 0f und der Prozentsatz für abgeschlossene Aufgaben 100f sein .
  2. Wenn zwei Aufgaben abgeschlossen und drei aktiv sind, sollte der abgeschlossene Prozentsatz bei 40f und der aktive Prozentsatz bei 60f liegen.

Schritt 2: Test für einen Programmfehler erstellen

Der Code für die getActiveAndCompletedStats wie geschrieben enthält einen Fehler. Sie wird nicht mehr richtig verarbeitet, wenn die Liste leer oder null ist. In beiden Fällen sollten beide Prozentsätze null sein.

internal fun getActiveAndCompletedStats(tasks: List<Task>?): StatsResult {

   val totalTasks = tasks!!.size
   val numberOfActiveTasks = tasks.count { it.isActive }
   val activePercent = 100 * numberOfActiveTasks / totalTasks
   val completePercent = 100 * (totalTasks - numberOfActiveTasks) / totalTasks

   return StatsResult(
       activeTasksPercent = activePercent.toFloat(),
       completedTasksPercent = completePercent.toFloat()
   )
  
}

Um den Code zu korrigieren und Tests zu schreiben, verwenden Sie die testbasierte Entwicklung. Gehen Sie dazu so vor:

  1. Schreiben Sie den Test unter Beachtung der Konvention, der Struktur und der gegebenen Uhrzeit und mit einem entsprechenden Namen.
  2. Prüfen Sie, ob der Test fehlschlägt.
  3. Schreiben Sie den Mindestcode, um den Test zu bestehen.
  4. Wiederholen Sie diesen Schritt für alle Tests.

Anstatt den Fehler zu beheben, schreiben Sie zuerst die Tests. So können Sie sicherstellen, dass Sie vor Tests versehentlich daran gehindert werden, diese Fehler zukünftig wieder zu sehen.

  1. Wenn eine leere Liste (emptyList()) vorhanden ist, müssen beide Prozentsätze 0f sein.
  2. Wenn beim Laden der Aufgaben ein Fehler aufgetreten ist, ist die Liste null und beide Prozentsätze sollten 0f sein.
  3. Führen Sie Ihre Tests aus und prüfen Sie, ob sie fehlschlagen:

Schritt 3: Fehler beheben

Nachdem Sie die Tests durchgeführt haben, können Sie den Fehler beheben.

  1. Behebe den Fehler in getActiveAndCompletedStats, indem du 0f zurückgibst, wenn tasks der Wert null oder leer ist:
internal fun getActiveAndCompletedStats(tasks: List<Task>?): StatsResult {

    return if (tasks == null || tasks.isEmpty()) {
        StatsResult(0f, 0f)
    } else {
        val totalTasks = tasks.size
        val numberOfActiveTasks = tasks.count { it.isActive }
        StatsResult(
            activeTasksPercent = 100f * numberOfActiveTasks / tasks.size,
            completedTasksPercent = 100f * (totalTasks - numberOfActiveTasks) / tasks.size
        )
    }
}
  1. Führen Sie Ihre Tests noch einmal aus und prüfen Sie, ob alle Tests bestanden wurden.

Indem du dich erst an TDD hält und die Tests schreibst, hast du Folgendes geholfen:

  • Neue Funktionen sind immer mit Tests verknüpft. So dienen Ihre Tests als Dokumentation der Codefunktion.
  • Die Tests sind auf die richtigen Ergebnisse ausgerichtet und bieten Schutz vor Fehlern, die Sie bereits erkannt haben.

Lösung: Weitere Tests schreiben

Hier sehen Sie alle Tests und den zugehörigen Funktionscode.

StatisticsUtilsTest.kt

class StatisticsUtilsTest {

    @Test
    fun getActiveAndCompletedStats_noCompleted_returnsHundredZero {
        val tasks = listOf(
            Task("title", "desc", isCompleted = false)
        )
        // When the list of tasks is computed with an active task
        val result = getActiveAndCompletedStats(tasks)

        // Then the percentages are 100 and 0
        assertThat(result.activeTasksPercent, `is`(100f))
        assertThat(result.completedTasksPercent, `is`(0f))
    }

    @Test
    fun getActiveAndCompletedStats_noActive_returnsZeroHundred() {
        val tasks = listOf(
            Task("title", "desc", isCompleted = true)
        )
        // When the list of tasks is computed with a completed task
        val result = getActiveAndCompletedStats(tasks)

        // Then the percentages are 0 and 100
        assertThat(result.activeTasksPercent, `is`(0f))
        assertThat(result.completedTasksPercent, `is`(100f))
    }

    @Test
    fun getActiveAndCompletedStats_both_returnsFortySixty() {
        // Given 3 completed tasks and 2 active tasks
        val tasks = listOf(
            Task("title", "desc", isCompleted = true),
            Task("title", "desc", isCompleted = true),
            Task("title", "desc", isCompleted = true),
            Task("title", "desc", isCompleted = false),
            Task("title", "desc", isCompleted = false)
        )
        // When the list of tasks is computed
        val result = getActiveAndCompletedStats(tasks)

        // Then the result is 40-60
        assertThat(result.activeTasksPercent, `is`(40f))
        assertThat(result.completedTasksPercent, `is`(60f))
    }

    @Test
    fun getActiveAndCompletedStats_error_returnsZeros() {
        // When there's an error loading stats
        val result = getActiveAndCompletedStats(null)

        // Both active and completed tasks are 0
        assertThat(result.activeTasksPercent, `is`(0f))
        assertThat(result.completedTasksPercent, `is`(0f))
    }

    @Test
    fun getActiveAndCompletedStats_empty_returnsZeros() {
        // When there are no tasks
        val result = getActiveAndCompletedStats(emptyList())

        // Both active and completed tasks are 0
        assertThat(result.activeTasksPercent, `is`(0f))
        assertThat(result.completedTasksPercent, `is`(0f))
    }
}

StatisticsUtils.kt

internal fun getActiveAndCompletedStats(tasks: List<Task>?): StatsResult {

    return if (tasks == null || tasks.isEmpty()) {
        StatsResult(0f, 0f)
    } else {
        val totalTasks = tasks.size
        val numberOfActiveTasks = tasks.count { it.isActive }
        StatsResult(
            activeTasksPercent = 100f * numberOfActiveTasks / tasks.size,
            completedTasksPercent = 100f * (totalTasks - numberOfActiveTasks) / tasks.size
        )
    }
}

Gut gemacht! Sie haben die Grundlagen des Schreibens und Ausführens von Tests erreicht. Als Nächstes erfahren Sie, wie Sie einfache ViewModel- und LiveData-Tests schreiben.

Im restlichen Codelab lernen Sie, wie Sie Tests für zwei Android-Kurse erstellen, die in den meisten Apps üblich sind: ViewModel und LiveData.

Sie beginnen mit den Tests für TasksViewModel.


Sie konzentrieren sich jetzt auf Tests, bei denen die gesamte Logik im Ansichtsmodell zum Einsatz kommt und nicht auf Repository-Code angewiesen ist. Der Repository-Code umfasst asynchronen Code, Datenbanken und Netzwerkaufrufe, die beide eine Komplexität in Bezug auf Tests erfordern. Sie möchten das jetzt nicht vermeiden und sich deshalb auf das Schreiben von Tests für ViewModel-Funktionen konzentrieren, die nicht direkt im Repository testen.



Der Test, den Sie schreiben, prüft, ob beim Aufrufen der addNewTask-Methode der Event zum Öffnen des neuen Aufgabenfensters ausgelöst wird. Hier ist der App-Code, den Sie testen möchten.

TasksViewModel.kt

fun addNewTask() {
   _newTaskEvent.value = Event(Unit)
}

Schritt 1: TasksViewModelTest-Klasse erstellen

Erstellen Sie wie in diesem Schritt für StatisticsUtilTest eine Testdatei für TasksViewModelTest.

  1. Öffne den Kurs, den du testen möchtest, im Paket tasks, TasksViewModel.
  2. Klicken Sie im Code mit der rechten Maustaste auf den Kursnamen TasksViewModel -> Generate -> Test.

  1. Klicken Sie auf dem Bildschirm Test erstellen auf OK, um die Annahme zu akzeptieren. Die Standardeinstellungen müssen nicht geändert werden.
  2. Wählen Sie im Dialogfeld Choose Directory Directory (Zielverzeichnis auswählen) das Verzeichnis test aus.

Schritt 2: ViewModel-Test schreiben

In diesem Schritt fügen Sie einen Modelltest für die Ansicht hinzu, um zu testen, ob beim Aufrufen der Methode addNewTask der Event geöffnet wird, um das neue Aufgabenfenster zu öffnen.

  1. Erstelle einen neuen Test namens „addNewTask_setsNewTaskEvent“.

TasksViewModelTest.kt

class TasksViewModelTest {

    @Test
    fun addNewTask_setsNewTaskEvent() {

        // Given a fresh TasksViewModel


        // When adding a new task


        // Then the new task event is triggered

    }
    
}

Wie verhält es sich mit dem Anwendungskontext?

Wenn Sie die zu testende Instanz TasksViewModel erstellen, benötigen Sie für ihren Konstruktor einen Anwendungskontext. Aber bei diesem Test erstellen Sie eine vollständige Anwendung mit Aktivitäten, UI und Fragmenten. Wie bekommen Sie also einen Anwendungskontext?

TasksViewModelTest.kt

// Given a fresh ViewModel
        val tasksViewModel = TasksViewModel(???)

Die AndroidX-Testbibliotheken enthalten Klassen und Methoden, die Ihnen Versionen von Komponenten wie zum Beispiel Apps und Aktivitäten zur Verfügung stellen. Wenn Sie einen lokalen Test haben, bei dem Sie simulierte Android-Framework-Klassen benötigen(z. B. einen Anwendungskontext), gehen Sie so vor, um Android X-Test korrekt einzurichten:

  1. AndroidX Test-Core- und ext-Abhängigkeiten hinzufügen
  2. Fügen Sie die Robolectric Testing Library hinzu.
  3. Annotieren Sie den Kurs mit dem AndroidJunit4-Test-Ausführer.
  4. AndroidX-Testcode schreiben

Sie führen diese Schritte durch und dann verstehen Sie, was sie gemeinsam bewirken.

Schritt 3: Gradle-Abhängigkeiten hinzufügen

  1. Kopieren Sie diese Abhängigkeiten in die build.gradle-Datei Ihres App-Moduls, um die zentralen CoreX-Testkern- und -Erweiterungsabhängigkeiten sowie die Robolectric-Testabhängigkeit hinzuzufügen.

app/build.gradle

    // AndroidX Test - JVM testing
testImplementation "androidx.test.ext:junit-ktx:$androidXTestExtKotlinRunnerVersion"

    testImplementation "androidx.test:core-ktx:$androidXTestCoreVersion"

 testImplementation "org.robolectric:robolectric:$robolectricVersion"

Schritt 4: JUnit Test-Ausführer hinzufügen

  1. Fügen Sie @RunWith(AndroidJUnit4::class)über dem Testkurs hinzu.

TasksViewModelTest.kt

@RunWith(AndroidJUnit4::class)
class TasksViewModelTest {
    // Test code
}

Schritt 5: AndroidX-Test verwenden

An diesem Punkt können Sie die AndroidX-Testbibliothek verwenden. Dazu gehört die Methode ApplicationProvider.getApplicationContext, die einen Anwendungskontext abruft.

  1. Erstelle eine TasksViewModel mit ApplicationProvider.getApplicationContext() aus der AndroidX-Testbibliothek.

TasksViewModelTest.kt

// Given a fresh ViewModel
val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
  1. Rufen Sie addNewTask unter tasksViewModel an.

TasksViewModelTest.kt

tasksViewModel.addNewTask()

Der Test sollte nun wie unten angegeben aussehen.

TasksViewModelTest.kt

    @Test
    fun addNewTask_setsNewTaskEvent() {

        // Given a fresh ViewModel
        val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())

        // When adding a new task
        tasksViewModel.addNewTask()

        // Then the new task event is triggered
        // TODO test LiveData
    }
  1. Prüfen Sie, ob der Test funktioniert.

Konzept: Wie funktioniert AndroidX Test?

Was ist AndroidX Test?

AndroidX Test ist eine Sammlung von Bibliotheken zum Testen. Er enthält Klassen und Methoden, mit denen Sie Versionen von Komponenten wie Anwendungen und Aktivitäten erhalten, die für Tests bestimmt sind. Der von Ihnen geschriebene Code ist beispielsweise ein Beispiel für eine AndroidX-Testfunktion zum Abrufen eines Anwendungskontexts.

ApplicationProvider.getApplicationContext()

Einer der Vorteile der AndroidX Test APIs besteht darin, dass sie sowohl für lokale Tests als auch für instrumentierte Tests funktionieren. Das ist aus folgenden Gründen hilfreich:

  • Sie können denselben Test wie einen lokalen Test oder einen instrumentierten Test durchführen.
  • Du musst nicht zwischen verschiedenen Test-APIs für lokale und instrumentierte Tests unterscheiden.

Da Sie z. B. Ihren Code mit AndroidX Test-Bibliotheken geschrieben haben, können Sie die TasksViewModelTest-Klasse aus dem Ordner test in den Ordner androidTest verschieben. Die Tests werden weiterhin ausgeführt. Die getApplicationContext() Funktionsweise kann leicht variieren, je nachdem, ob sie als lokaler oder instrumentierter Test ausgeführt wird:

  • Wenn es einen instrumentierten Test gibt, wird der tatsächliche Anwendungskontext abgerufen, wenn ein Emulator gestartet oder eine Verbindung zu einem echten Gerät hergestellt wird.
  • Wenn es sich um einen lokalen Test handelt, wird eine simulierte Android-Umgebung verwendet.

Was ist Robolectric?

Die simulierte Android-Umgebung, die AndroidX Test für lokale Tests verwendet, wird von Robolectric bereitgestellt. Robolectric ist eine Bibliothek, die eine simulierte Android-Umgebung für Tests erstellt und schneller ausgeführt wird, als einen Emulator zu starten oder auf einem Gerät auszuführen. Ohne die Robolectric-Abhängigkeit erhalten Sie diesen Fehler:

Was genau macht @RunWith(AndroidJUnit4::class)?

Ein Test-Ausführer ist eine JUnit-Komponente, die Tests ausführt. Ohne einen Test-Ausführer werden Ihre Tests nicht ausgeführt. Es gibt einen Standard-Test-Ausführer, den Sie von JUnit erhalten und den Sie automatisch erhalten. @RunWith tauscht diesen Standard-Test-Ausführer aus.

Mit dem AndroidJUnit4-Test-Ausführer kann der AndroidX-Test unterschiedlich durchgeführt werden, je nachdem, ob es sich um instrumentierte oder lokale Tests handelt.

Schritt 6: Robolektrische Warnungen beheben

Beim Ausführen des Codes wird Robolectric verwendet.

Dank AndroidX Test und des AndroidJunit4-Test-Ausführers ist das nicht erforderlich, weil du zum Beispiel keinen Robolectric-Code schreiben musst.

Möglicherweise sehen Sie zwei Warnungen.

  • No such manifest file: ./AndroidManifest.xml
  • "WARN: Android SDK 29 requires Java 9..."

Du kannst die No such manifest file: ./AndroidManifest.xml-Warnung beheben, indem du deine Gradle-Datei aktualisierst.

  1. Fügen Sie die folgende Zeile in Ihre Gradle-Datei ein, damit das richtige Android-Manifest verwendet wird. Mit der Option includeAndroidResources können Sie auf Android-Ressourcen in Ihren Einheitentests zugreifen, einschließlich Ihrer AndroidManifest-Datei.

app/build.gradle

    // Always show the result of every unit test when running via command line, even if it passes.
    testOptions.unitTests {
        includeAndroidResources = true

        // ... 
    }

Die Warnung "WARN: Android SDK 29 requires Java 9..." ist komplizierter. Für Tests mit Android Q ist Java 9 erforderlich. Statt zu versuchen, Android Studio für die Verwendung von Java 9 zu konfigurieren, behalten Sie bei diesem Codelab Ihren Wert und kompilieren Sie das SDK bei 28.

Zusammenfassung:

  • Reine Modelltests können normalerweise in der test-Quelle erfolgen, weil für ihren Code normalerweise Android nicht erforderlich ist.
  • In der AndroidX-Testbibliothek können Sie Testversionen von Komponenten wie Apps und Aktivitäten abrufen.
  • Wenn du simulierten Android-Code in deinem test-Quellsatz ausführen musst, kannst du die Robolectric-Abhängigkeit und die Annotation @RunWith(AndroidJUnit4::class) hinzufügen.

Herzlichen Glückwunsch, du hast sowohl die AndroidX-Testbibliothek als auch Robolectric genutzt, um einen Test durchzuführen. Dein Test ist noch nicht abgeschlossen. Du hast noch keine rechtliche Erklärung geschrieben, er sagt aber nur // TODO test LiveData. Du lernst dann, mit LiveData Aussagen zu formulieren.

In dieser Aufgabe lernen Sie, wie Sie den Wert von LiveData korrekt geltend machen.

Hier, ohne vorher addNewTask_setsNewTaskEvent-Modelltest anzusehen,

TasksViewModelTest.kt

    @Test
    fun addNewTask_setsNewTaskEvent() {

        // Given a fresh ViewModel
        val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())

        // When adding a new task
        tasksViewModel.addNewTask()

        // Then the new task event is triggered
        // TODO test LiveData
    }
    

Zum Testen von LiveData kannst du Folgendes tun:

  1. InstantTaskExecutorRule verwenden
  2. LiveData Beobachtung beobachten

Schritt 1: InstantTaskExecutorRule verwenden

InstantTaskExecutorRule ist eine JUnit-Regel. Wenn Sie diese Kombination mit der Annotation @get:Rule verwenden, wird vor und nach den Tests Code in der Klasse InstantTaskExecutorRule ausgeführt. Den genauen Code sehen Sie mit der Tastenkombination Befehlstaste + B.

Mit dieser Regel werden alle Hintergrundjobs, die mit der Architektur zusammenhängen, in demselben Thread ausgeführt, sodass die Testergebnisse synchron und in einer wiederholten Reihenfolge ausgeführt werden. Verwenden Sie diese Regel, wenn Sie Tests zum Testen von LiveData schreiben

  1. Fügen Sie die Gradle-Abhängigkeit der Core-Testbibliothek für Komponenten der Architektur hinzu, die diese Regel enthält.

app/build.gradle

testImplementation "androidx.arch.core:core-testing:$archTestingVersion"
  1. TasksViewModelTest.kt öffnen
  2. Füge InstantTaskExecutorRule innerhalb der Klasse TasksViewModelTest hinzu.

TasksViewModelTest.kt

class TasksViewModelTest {
    @get:Rule
    var instantExecutorRule = InstantTaskExecutorRule()
    
    // Other code...
}

Schritt 2: Klasse „LiveDataTestUtil.kt“ hinzufügen

Als Nächstes sollten Sie dafür sorgen, dass der LiveData, den Sie testen, beobachtet wird.

Wenn du LiveData verwendest, nutzt du häufig eine Aktivität oder ein Fragment (LifecycleOwner), um LiveData zu beobachten.

viewModel.resultLiveData.observe(fragment, Observer {
    // Observer code here
})

Diese Beobachtung ist wichtig. Sie benötigen aktive Betrachter auf LiveData

Damit das erwartete LiveData-Verhalten für Ihr Datenansichtsmodell LiveData angezeigt wird, müssen Sie LiveData mit LifecycleOwner beobachten.

Dies stellt ein Problem dar: In deinem TasksViewModel-Test hast du keine Aktivität oder ein Fragment, um deine LiveData zu beobachten. Sie können das umgehen: Verwenden Sie die observeForever-Methode, mit der LiveData kontinuierlich beobachtet wird, ohne dass eine LifecycleOwner erforderlich ist. Wenn Sie observeForever verwenden, müssen Sie daran denken, Ihren Observer zu entfernen oder ein Beschädigung zu vermeiden.

Das sieht in etwa so aus: So können Sie dies prüfen:

@Test
fun addNewTask_setsNewTaskEvent() {

    // Given a fresh ViewModel
    val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())


    // Create observer - no need for it to do anything!
    val observer = Observer<Event<Unit>> {}
    try {

        // Observe the LiveData forever
        tasksViewModel.newTaskEvent.observeForever(observer)

        // When adding a new task
        tasksViewModel.addNewTask()

        // Then the new task event is triggered
        val value = tasksViewModel.newTaskEvent.value
        assertThat(value?.getContentIfNotHandled(), (not(nullValue())))

    } finally {
        // Whatever happens, don't forget to remove the observer!
        tasksViewModel.newTaskEvent.removeObserver(observer)
    }
}

Das ist ein Boilerplate-Code, mit dem sich eine einzelne LiveData in einem Test beobachten lässt. Es gibt mehrere Möglichkeiten, diesen Standardtext zu entfernen. Sie erstellen eine Erweiterungsfunktion namens LiveDataTestUtil, um das Hinzufügen von Betrachtern zu vereinfachen.

  1. Erstellen Sie in Ihrem test-Quellsatz eine neue Kotlin-Datei mit dem Namen LiveDataTestUtil.kt.


  1. Kopieren Sie den Code unten und fügen Sie ihn ein.

LiveDataTestUtil.kt

import androidx.annotation.VisibleForTesting
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException


@VisibleForTesting(otherwise = VisibleForTesting.NONE)
fun <T> LiveData<T>.getOrAwaitValue(
    time: Long = 2,
    timeUnit: TimeUnit = TimeUnit.SECONDS,
    afterObserve: () -> Unit = {}
): T {
    var data: T? = null
    val latch = CountDownLatch(1)
    val observer = object : Observer<T> {
        override fun onChanged(o: T?) {
            data = o
            latch.countDown()
            this@getOrAwaitValue.removeObserver(this)
        }
    }
    this.observeForever(observer)

    try {
        afterObserve.invoke()

        // Don't wait indefinitely if the LiveData is not set.
        if (!latch.await(time, timeUnit)) {
            throw TimeoutException("LiveData value was never set.")
        }

    } finally {
        this.removeObserver(observer)
    }

    @Suppress("UNCHECKED_CAST")
    return data as T
}

Das ist eine ziemlich komplizierte Methode. Es erstellt eine Kotlin-Erweiterungsfunktion mit der Bezeichnung getOrAwaitValue, die einen Observer hinzufügt, den LiveData-Wert erhält und dann den Observer bereinigt – im Grunde eine kurze, wiederverwendbare Version des oben dargestellten observeForever-Codes. Eine vollständige Erklärung dieses Kurses findest du in diesem Blogpost.

Schritt 3: Verwenden Sie getOrAwaitValue, um die Assertion zu schreiben.

In diesem Schritt verwenden Sie die Methode getOrAwaitValue und schreiben eine Assertion, mit der geprüft wird, ob newTaskEvent ausgelöst wurde.

  1. Rufen Sie den Wert für LiveData für newTaskEvent mit getOrAwaitValue ab.
val value = tasksViewModel.newTaskEvent.getOrAwaitValue()
  1. Gelten Sie, dass der Wert nicht null ist.
assertThat(value.getContentIfNotHandled(), (not(nullValue())))

Der vollständige Test sollte wie unten angegeben aussehen.

import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.example.android.architecture.blueprints.todoapp.getOrAwaitValue
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.not
import org.hamcrest.Matchers.nullValue
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class TasksViewModelTest {

    @get:Rule
    var instantExecutorRule = InstantTaskExecutorRule()


    @Test
    fun addNewTask_setsNewTaskEvent() {
        // Given a fresh ViewModel
        val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())

        // When adding a new task
        tasksViewModel.addNewTask()

        // Then the new task event is triggered
        val value = tasksViewModel.newTaskEvent.getOrAwaitValue()

        assertThat(value.getContentIfNotHandled(), not(nullValue()))


    }

}
  1. Führen Sie Ihren Code aus und beobachten Sie die Prüfung.

Nachdem Sie jetzt gesehen haben, wie ein Test geschrieben wird, schreiben Sie selbst einen. In diesem Schritt kannst du anhand der gelernten Fertigkeiten noch einmal einen TasksViewModel-Test schreiben.

Schritt 1: Eigenen ViewModel-Test schreiben

Sie schreiben setFilterAllTasks_tasksAddViewVisible(). Mit diesem Test sollten Sie prüfen, ob die Schaltfläche Aufgabe hinzufügen angezeigt wird, wenn Sie den Filtertyp so einstellen, dass alle Aufgaben angezeigt werden.

  1. Verwende als Referenz für addNewTask_setsNewTaskEvent()einen Test in TasksViewModelTest setFilterAllTasks_tasksAddViewVisible(), der den Filtermodus auf ALL_TASKS setzt und bestätigt, dass die tasksAddViewVisible LiveData true sind.


Verwenden Sie den Code unten, um loszulegen.

TasksViewModelTest.

    @Test
    fun setFilterAllTasks_tasksAddViewVisible() {

        // Given a fresh ViewModel

        // When the filter type is ALL_TASKS

        // Then the "Add task" action is visible
        
    }

Hinweis:

  • Die Aufzählung TasksFilterType für alle Aufgaben ist ALL_TASKS.
  • Die Sichtbarkeit der Schaltfläche zum Hinzufügen einer Aufgabe wird vom LiveData tasksAddViewVisible. bestimmt
  1. Führen Sie den Test aus.

Schritt 2: Test mit der Lösung vergleichen

Vergleichen Sie Ihre Lösung mit der folgenden Lösung.

TasksViewModelTest.

    @Test
    fun setFilterAllTasks_tasksAddViewVisible() {

        // Given a fresh ViewModel
        val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())

        // When the filter type is ALL_TASKS
        tasksViewModel.setFiltering(TasksFilterType.ALL_TASKS)

        // Then the "Add task" action is visible
        assertThat(tasksViewModel.tasksAddViewVisible.getOrAwaitValue(), `is`(true))
    }

Prüfen Sie, ob Sie

  • Du erstellst die tasksViewModel mit derselben AndroidX-ApplicationProvider.getApplicationContext()-Anweisung.
  • Sie rufen die Methode setFiltering auf und übergeben die Aufzählung des Filtertyps ALL_TASKS.
  • Mit der Methode getOrAwaitNextValue bestätigst du, dass die tasksAddViewVisible wahr ist.

Schritt 3: @Vorher-Regel hinzufügen

Sie sehen, wie zu Beginn beider Tests eine TasksViewModel definiert.

TasksViewModelTest.

        // Given a fresh ViewModel
        val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())

Wenn Sie wiederholten Einrichtungscode für mehrere Tests haben, können Sie mit der Annotation @Before eine Einrichtungsmethode erstellen und wiederholten Code entfernen. Bei allen Tests wird TasksViewModel getestet und es ist ein Aufrufmodell erforderlich. Du kannst diesen Code daher in einen @Before-Block verschieben.

  1. Erstellen Sie eine lateinit-Instanzvariable mit dem Namen tasksViewModel|.
  2. Erstellen Sie eine Methode namens setupViewModel.
  3. Zeichne sie mit @Before an.
  4. Verschieben Sie den Aufruf der Instanzmodellansicht nach setupViewModel.

TasksViewModelTest.

    // Subject under test
    private lateinit var tasksViewModel: TasksViewModel

    @Before
    fun setupViewModel() {
        tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
    }
  1. Führen Sie Ihren Code aus.

Warnung

Führen Sie nicht Folgendes aus und initialisieren Sie

tasksViewModel

mit ihrer Definition:

val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())

Dadurch wird für alle Tests dieselbe Instanz verwendet. Das sollten Sie vermeiden, da jeder Test eine aktuelle Instanz des getesteten Themas haben sollte (in diesem Fall „ViewModel“).

Der endgültige Code für TasksViewModelTest sollte so aussehen:

TasksViewModelTest

@RunWith(AndroidJUnit4::class)
class TasksViewModelTest {

    // Subject under test
    private lateinit var tasksViewModel: TasksViewModel

    // Executes each task synchronously using Architecture Components.
    @get:Rule
    var instantExecutorRule = InstantTaskExecutorRule()

    @Before
    fun setupViewModel() {
        tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
    }


    @Test
    fun addNewTask_setsNewTaskEvent() {

        // When adding a new task
        tasksViewModel.addNewTask()

        // Then the new task event is triggered
        val value = tasksViewModel.newTaskEvent.awaitNextValue()
        assertThat(
            value?.getContentIfNotHandled(), (not(nullValue()))
        )
    }

    @Test
    fun getTasksAddViewVisible() {

        // When the filter type is ALL_TASKS
        tasksViewModel.setFiltering(TasksFilterType.ALL_TASKS)

        // Then the "Add task" action is visible
        assertThat(tasksViewModel.tasksAddViewVisible.awaitNextValue(), `is`(true))
    }
    
}

Klicken Sie hier, um den Unterschied zwischen dem eingegebenen Code und dem endgültigen Code zu sehen.

Mit dem folgenden Git-Befehl können Sie den Code des fertigen Codelabs herunterladen:

$ git clone https://github.com/googlecodelabs/android-testing.git
$ cd android-testing
$ git checkout end_codelab_1


Alternativ können Sie das Repository als ZIP-Datei herunterladen, entpacken und in Android Studio öffnen.

Zip herunterladen

In diesem Codelab ging es um folgende Themen:

  • Wie Sie Tests in Android Studio durchführen
  • Der Unterschied zwischen lokalen (test) und Instrumentierungstests (androidTest).
  • So schreiben Sie lokale Einheitentests mit JUnit und Hamcrest.
  • ViewModel-Tests mit der AndroidX-Testbibliothek einrichten

Udacity-Kurs:

Android-Entwicklerdokumentation:

Videos:

Sonstiges:

Links zu weiteren Codelabs in diesem Kurs finden Sie auf der Landingpage des erweiterten Android-Tools in Kotlin.