Skip to main content
April 13, 2022

User Interfaces in Kotlin testen | Wie man sie implementiert

Wenn wir uns die Reihe der Code Labs „Advanced Android in Kotlin“ auf Google ansehen, finden wir eines über Testing und Dependency Injection. Es ist nicht nur eine Testpraxis, sondern auch ein Architekturleitfaden für Android.

Wenn Sie zum dritten Abschnitt kommen, werden Sie anfangen, UI-Tests mit Espresso zu schreiben. Es ist großartig, sie auszuführen und zu sehen, wie die App von selbst läuft und Dinge tut. Obwohl als fortgeschrittenes Code-Lab archiviert, kann der Code für den Test mit dem Page Object Pattern refaktorisiert werden.

Seitenobjekt-Muster

Der Zweck eines Seitenobjekts ist es, den „fremden“ Code zu verstecken, der benötigt wird, um die Schritte zu replizieren, die notwendig sind, um in unserer Anwendung zu navigieren, Aktionen als Benutzer auszuführen und eine saubere Schnittstelle bereitzustellen, mit der nützliche und sinnvolle Tests erstellt werden können.

Um dies in Aktion zu sehen, werden wir den Code aus dem End Solution Lab Code Repo verwenden, der hier zu finden ist.

Wir werden mit dem Zweig „end_codelab_3“ arbeiten.

Seitenobjekt in Kotlin

Wir beginnen mit dem Hinzufügen der Kotlin base Page-Klasse, die die Magie in Gang setzen wird. Fügen Sie diese Klasse zum Quellenset „androidTest“ hinzu:

open class Page {

companion object {

inline fun <reified T : Page> on(): T {

return Page().on()

}

}

inline fun <reified T : Page> on(): T {

val page = T::class.constructors.first().call()

page.verify()

//Thread.sleep(500) //Zur Überbrückung bei asynchronen Aufrufen

return page

}

open fun verify(): Page {

// Jede Unterseite sollte hier ihre Standardgarantien haben

return this

}

fun back(): Page {

Espresso.pressBack()

return this

}

}

Wenn Sie einen genaueren Blick auf Page Object Pattern in Kotlin for UI Test Automation On Android von Kate Savelova werfen wollen.

Letztendlich dient diese Klasse dazu, eine flüssige API zu definieren, die uns dabei hilft, den Code zu organisieren, der für die Navigation, Aktionen und Prüfungen auf den Seiten, Ansichten usw. unserer App benötigt wird. Achten Sie besonders auf die Verify-Funktion. Diese Funktion wird verwendet, um zu prüfen, ob die gewünschte Seite geladen wurde.

Beginnen wir damit, unseren ersten Test mit dieser Seite hinzuzufügen. Gehen Sie zur Datei AppNavigationTest.kt und fügen Sie einen neuen Test hinzu, der der App eine neue Aufgabe hinzufügt.

@Test

fun createNewTask() {

// Starten des Bildschirms Aufgaben

val activityScenario = ActivityScenario.launch(TasksActivity::class.java)

dataBindingIdlingResource.monitorActivity(activityScenario)

 

// Wenn Sie ActivityScenario.launch verwenden, rufen Sie immer close() auf

activityScenario.close()

}

Dies ist der Startcode für den Test, der gerade die TasksActivity gestartet hat. Für ein tieferes Verständnis schauen Sie sich das Codelabor hier an.

Der vollständige Code für den Test lautet:

@Test

fun createNewTask() {

// Starten des Bildschirms Tasks

val activityScenario = ActivityScenario.launch(TasksActivity::class.java)

dataBindingIdlingResource.monitorActivity(activityScenario)

 

val testTask= Aufgabe(„Titel“, „Beschreibung“)

 

Page.on<TasksPage>()

.tapOnAddButton()

.on<AddTaskPage>()

.addTask(testTask)

.on<AufgabenSeite>()

.checkAddedTask(testTask)

 

// Wenn Sie ActivityScenario.launch verwenden, rufen Sie immer close() auf

activityScenario.close()

}

Sehen Sie sich die Schönheit dieses Tests an:

    • Erstellen Sie eine Aufgabe
    • Klicken Sie auf der Aufgabenseite auf die Schaltfläche Hinzufügen
    • Auf der Seite AddTaskPage fügen Sie die Aufgabe hinzu, die wir in Schritt 1 erstellt haben
    • Überprüfen Sie nun auf der Aufgabenseite, ob die Aufgabe hinzugefügt wurde

 

Dies ist sehr sinnvoll und einfach. Es lässt sich noch nicht kompilieren, aber keine Sorge. Wir werden das alles noch in Ordnung bringen:

Fügen Sie die gradle-Abhängigkeit zur build.gradle-Anwendung hinzu:

  • Implementierung „org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion“

TasksPage und AddTaskPage Klassen

Fügen Sie die Klassen TasksPage und AddTaskPage in das gleiche PageObject-Paket ein:

class TasksPage : Page() {

override fun verify(): TasksPage {

onView(withId(R.id.tasks_container_layout))

.check(matches(isDisplayed()))

return this

}

fun tapOnAddButton(): TasksPage {

onView(withId(R.id.add_task_fab)).perform(ViewActions.click())

return this

}

 

fun tapOnEditTask(): TasksPage {

onView(withId(R.id.edit_task_fab)).perform(ViewActions.click())

return this

}

 

fun checkAddedTask(testTask: Aufgabe): TasksPage {

onView(withText(testTask.title))

return this

}

}

 

class AddTaskPage: Seite() {

override fun verify(): AddTaskPage {

Espresso.onView(withId(R.id.add_task_title_edit_text))

.check(ViewAssertions.matches(isDisplayed())))

return this

}

 

fun addTask(task: Task):AddTaskPage{

onView(withId(R.id.add_task_title_edit_text))

.perform(clearText(), typeText(task.title))

onView(withId(R.id.add_task_description_edit_text))

.perform(clearText(), typeText(task.description))

onView(withId(R.id.save_task_fab)).perform(click())

return this

}

}

Enthält den gesamten Espresso-Code, der für die Durchführung aller Interaktionen erforderlich ist. Wenn wir dieses Muster nicht verwenden, haben wir am Ende einen sehr langen Test mit all diesen Espresso-Methoden in einem einzigen Test, was das Lesen und die Wartung sehr erschwert.

Am Ende läuft diese Anwendung auf dem Handy:

 

Der äquivalente Test ohne die Verwendung des Page Object-Musters lautet:

@Test

fun createNewTaskWithoutPageObject(){

val activityScenario = ActivityScenario.launch(TasksActivity::class.java)

dataBindingIdlingResource.monitorActivity(activityScenario)

 

val task= Aufgabe(„Titel“, „Beschreibung“)

 

//Überprüfen der geöffneten Aufgabenseite

onView(withId(R.id.tasks_container_layout))

.check(matches(isDisplayed())))

 

//Tippen Sie auf die Schaltfläche Hinzufügen

onView(withId(R.id.add_task_fab)).perform(ViewActions.click())

 

//Überprüfen, ob die Aufgabenseite geöffnet ist

onView(withId(R.id.add_task_title_edit_text))

.check(ViewAssertions.matches(isDisplayed())))

 

//Aufgabe hinzufügen

onView(withId(R.id.add_task_title_edit_text))

.perform(ViewActions.clearText(), ViewActions.typeText(task.title))

onView(withId(R.id.add_task_description_edit_text))

.perform(ViewActions.clearText(), ViewActions.typeText(task.description))

onView(withId(R.id.save_task_fab)).perform(click())

 

//Überprüfen, ob die Aufgabenseite geöffnet ist

onView(withId(R.id.tasks_container_layout))

.check(matches(isDisplayed())))

 

//überprüfe hinzugefügte Aufgabe

onView(withText(task.title))

 

// Bei Verwendung von ActivityScenario.launch, immer close() aufrufen

activityScenario.close()

}

Welchen Testcode bevorzugen Sie?

Die Vorteile der Verwendung dieses Musters zur Erstellung von UI-Tests sind:

  • Sinnvolle UI-Tests:
    • Wie Sie sehen können, ist der UI-Testcode sehr anschaulich, er ist wie ein Roman geschrieben.
  • Wartung:
    • Jedes Mal, wenn sich die Benutzeroberfläche ändert, müssen wir nur die Seiten ändern, die von der Änderung betroffen sind. Aber mit dieser Architektur ist es einfach, sie zu finden und herauszufinden, warum.
    • Das Hinzufügen neuer Tests wird einfach, um doppelten Code zu vermeiden.

Zusammenfassungen

Page Object ist ein großartiges Muster, das für UI-Tests auf anderen Plattformen wie Java, JavaScript, C# usw. weit verbreitet ist. Und wie Sie sehen können, ist es sehr einfach, wenn Sie es einmal verstanden haben. Wir hoffen, dass Sie diesen Beitrag nützlich fanden!

Sie können eine vollständige Version dieses Codes in meiner Google Code Fork sehen.

Und wenn Sie ein Ktolin-Experte werden wollen, sollten Sie diese Sitzung mit meinem Kollegen Miguel Ángel Barrera nicht verpassen, in der wir über alle Geheimnisse von Ktolin sprechen werden.