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:
- Die Kotlin-Programmiersprache
- Die folgenden Android-Jetpack-Bibliotheken:
ViewModel
undLiveData
- Anwendungsarchitektur gemäß dem Muster aus dem Leitfaden zur App-Architektur und den Codelabs für Android-Grundlagen
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
- undViewModel
-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:
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:
- Raum mit einem Open Codelab
- Schulungs-Codelabs für Android Kotlin Fundamentals
- Erweiterte Android-Codelabs
- Android-Beispiel für Sonnenblumen
- Android-Apps mit Kotlin-Udacity-Schulungskurs entwickeln
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: | |
| Aufgabenbildschirm hinzufügen oder bearbeiten:UI-Ebenencode zum Hinzufügen oder Bearbeiten einer Aufgabe. |
| Datenschicht: Dies ist die Datenschicht der Aufgaben. Es enthält den Datenbank-, Netzwerk- und Repository-Code. |
| Statistikbildschirm: UI-Ebenencode für den Statistikbildschirm |
| Bildschirm mit den Aufgabendetails:UI-Ebenencode für eine einzelne Aufgabe. |
| Auf dem Bildschirm „Aufgaben“: Code der UI-Ebene für die Liste aller Aufgaben. |
| 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.
- Ö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
- Öffnen Sie den Ordner
test
, bis Sie die Datei ExampleUnitTest.kt finden. - 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:
- 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.
- Fügen Sie
assertEquals(3, 1 + 1)
zumaddition_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
}
}
- Test ausführen
- Neben den Testergebnissen ist ein „X“ zu sehen.
- 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.
- Öffnen Sie den Quellsatz
androidTest
. - 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
- Öffnen Sie in der
main
-Quelle intodoapp.statistics
StatisticsUtils.kt
. - 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.
- Klicken Sie mit der rechten Maustaste auf
getActiveAndCompletedStats
und wählen Sie Erstellen &Testen aus.
Das Dialogfeld Test erstellen wird geöffnet:
- Ändern Sie den Kursnamen in
StatisticsUtilsTest
(anstattStatisticsUtilsKtTest
; es ist etwas schöner, wenn KT im Namen der Testklasse nicht verwendet wird). - 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. - 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.
- Wählen Sie das
test
-Verzeichnis aus (nichtandroidTest
), da Sie lokale Tests schreiben. - Klicken Sie auf OK.
- Sie sehen, dass die Klasse
StatisticsUtilsTest
intest/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%.
- Öffnen Sie
StatisticsUtilsTest
. - 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
}
}
- Fügen Sie oberhalb des Funktionsnamens die Annotation
@Test
hinzu, um sie als Test zu kennzeichnen. - Eine Liste mit Aufgaben erstellen
// Create an active task
val tasks = listOf<Task>(
Task("title", "desc", isCompleted = false)
)
- Rufen Sie
getActiveAndCompletedStats
mit diesen Aufgaben auf.
// Call your function
val result = getActiveAndCompletedStats(tasks)
- 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)
}
}
- 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.
- Ö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 imandroidTest
-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
- Aktualisieren Sie den
getActiveAndCompletedStats_noCompleted_returnsHundredZero()
-Test, um HamcrestassertThat
anstelle vonassertEquals
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))
}
}
- 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:
- Wenn es eine abgeschlossene Aufgabe und keine aktiven Aufgaben gibt, sollte der Prozentsatz für
activeTasks
0f
und der Prozentsatz für abgeschlossene Aufgaben100f
sein . - Wenn zwei Aufgaben abgeschlossen und drei aktiv sind, sollte der abgeschlossene Prozentsatz bei
40f
und der aktive Prozentsatz bei60f
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:
- Schreiben Sie den Test unter Beachtung der Konvention, der Struktur und der gegebenen Uhrzeit und mit einem entsprechenden Namen.
- Prüfen Sie, ob der Test fehlschlägt.
- Schreiben Sie den Mindestcode, um den Test zu bestehen.
- 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.
- Wenn eine leere Liste (
emptyList()
) vorhanden ist, müssen beide Prozentsätze 0f sein. - Wenn beim Laden der Aufgaben ein Fehler aufgetreten ist, ist die Liste
null
und beide Prozentsätze sollten 0f sein. - 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.
- Behebe den Fehler in
getActiveAndCompletedStats
, indem du0f
zurückgibst, wenntasks
der Wertnull
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
)
}
}
- 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
.
- Öffne den Kurs, den du testen möchtest, im Paket
tasks
,TasksViewModel.
- Klicken Sie im Code mit der rechten Maustaste auf den Kursnamen
TasksViewModel
-> Generate -> Test.
- Klicken Sie auf dem Bildschirm Test erstellen auf OK, um die Annahme zu akzeptieren. Die Standardeinstellungen müssen nicht geändert werden.
- 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.
- 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:
- AndroidX Test-Core- und ext-Abhängigkeiten hinzufügen
- Fügen Sie die Robolectric Testing Library hinzu.
- Annotieren Sie den Kurs mit dem AndroidJunit4-Test-Ausführer.
- AndroidX-Testcode schreiben
Sie führen diese Schritte durch und dann verstehen Sie, was sie gemeinsam bewirken.
Schritt 3: Gradle-Abhängigkeiten hinzufügen
- 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
- 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.getApplicationContex
t
, die einen Anwendungskontext abruft.
- Erstelle eine
TasksViewModel
mitApplicationProvider.getApplicationContext()
aus der AndroidX-Testbibliothek.
TasksViewModelTest.kt
// Given a fresh ViewModel
val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
- Rufen Sie
addNewTask
untertasksViewModel
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
}
- 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.
- 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:
InstantTaskExecutorRule
verwendenLiveData
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
- 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"
TasksViewModelTest.kt
öffnen- Füge
InstantTaskExecutorRule
innerhalb der KlasseTasksViewModelTest
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
- Alle
onChanged
-Ereignisse auslösen - Lösen Sie alle Transformationen aus.
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.
- Erstellen Sie in Ihrem
test
-Quellsatz eine neue Kotlin-Datei mit dem NamenLiveDataTestUtil.kt
.
- 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.
- Rufen Sie den Wert für
LiveData
fürnewTaskEvent
mitgetOrAwaitValue
ab.
val value = tasksViewModel.newTaskEvent.getOrAwaitValue()
- 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()))
}
}
- 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.
- Verwende als Referenz für
addNewTask_setsNewTaskEvent()
einen Test inTasksViewModelTest
setFilterAllTasks_tasksAddViewVisible()
, der den Filtermodus aufALL_TASKS
setzt und bestätigt, dass dietasksAddViewVisible
LiveDatatrue
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 istALL_TASKS.
- Die Sichtbarkeit der Schaltfläche zum Hinzufügen einer Aufgabe wird vom
LiveData
tasksAddViewVisible.
bestimmt
- 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 FiltertypsALL_TASKS
. - Mit der Methode
getOrAwaitNextValue
bestätigst du, dass dietasksAddViewVisible
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.
- Erstellen Sie eine
lateinit
-Instanzvariable mit dem NamentasksViewModel|
. - Erstellen Sie eine Methode namens
setupViewModel
. - Zeichne sie mit
@Before
an. - Verschieben Sie den Aufruf der Instanzmodellansicht nach
setupViewModel
.
TasksViewModelTest.
// Subject under test
private lateinit var tasksViewModel: TasksViewModel
@Before
fun setupViewModel() {
tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
}
- 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.
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:
- Leitfaden zur App-Architektur
- JUnit4
- Hamcrest
- Robolectric Testing Library
- Android X-Testbibliothek
- Core-Bibliothek für AndroidX-Architekturkomponenten
- Quellsätze
- Über die Befehlszeile testen
Videos:
Sonstiges:
Links zu weiteren Codelabs in diesem Kurs finden Sie auf der Landingpage des erweiterten Android-Tools in Kotlin.