In diesem Beitrag erfahren Sie, wie Sie UI-Tests mit dem Espresso-Test-Framework schreiben und Ihren Test-Workflow automatisieren, anstatt den langwierigen und äußerst fehleranfälligen manuellen Prozess zu verwenden.
Espresso ist ein Test-Framework zum Schreiben von UI-Tests in Android. Den offiziellen Dokumenten zufolge können Sie:
Verwenden Sie Espresso, um präzise, schöne und zuverlässige Android-UI-Tests zu schreiben.
Ein Problem beim manuellen Testen ist, dass es zeitaufwendig und langwierig sein kann. Um beispielsweise einen Anmeldebildschirm (manuell) in einer Android-App zu testen, müssen Sie Folgendes tun:
BenutzernameEditText
und passwordEditText
sind sichtbar. Anstatt all die Zeit damit zu verbringen, unsere App manuell zu testen, sollten Sie mehr Zeit damit verbringen, Code zu schreiben, durch den sich unsere App von den anderen abhebt! Auch wenn manuelle Tests langwierig und ziemlich langsam sind, sind sie dennoch fehleranfällig und Sie können einige Eckpunkte übersehen.
Einige Vorteile des automatisierten Testens umfassen Folgendes:
In diesem Lernprogramm erfahren Sie mehr über Espresso, indem Sie es in ein Android Studio-Projekt integrieren. Wir schreiben UI-Tests für einen Anmeldebildschirm und eine RecyclerView
, und wir lernen etwas über das Testen von Absichten.
Qualität ist keine Tat, es ist eine Gewohnheit. - Pablo Picasso
Um diesem Tutorial folgen zu können, benötigen Sie:
Ein Beispielprojekt (in Kotlin) für dieses Lernprogramm finden Sie in unserem GitHub-Repo, sodass Sie es einfach mitverfolgen können.
Starten Sie Ihr Android Studio 3 und erstellen Sie ein neues Projekt mit einer leeren Aktivität Hauptaktivität
. Stellen Sie sicher, dass Sie es überprüfen Kotlin-Unterstützung einschließen.
AndroidJUnitRunner
Fügen Sie nach dem Erstellen eines neuen Projekts die folgenden Abhängigkeiten aus der Android Testing Support Library in Ihrem hinzu build.gradle (obwohl Android Studio sie bereits für uns aufgenommen hat). In diesem Tutorial verwenden wir die neueste Espresso-Bibliotheksversion 3.0.2 (zum Zeitpunkt des Schreibens)..
android //… defaultConfig //… testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" //… Abhängigkeiten //… androidTestImplementation 'com.android.support.test.espresso: espresso-core: 3.0. 2 'androidTestImplementation' com.android.support.test: Läufer: 1.0.2 'androidTestImplementation' com.android.support.test: Regeln: 1.0.2 '
Wir haben auch den Instrumentation Runner dabei AndroidJUnitRunner
:
Ein Instrumentierung
die JUnit3- und JUnit4-Tests mit einem Android-Paket ausführt (Anwendung).
Beachten Sie, dass Instrumentierung
ist einfach eine Basisklasse zum Implementieren von Code für Anwendungsinstrumente.
Die Synchronisation von Espresso, die nicht auf das Ende einer Animation warten kann, kann dazu führen, dass einige Tests fehlschlagen, wenn Sie Animationen auf Ihrem Testgerät zulassen. Um die Animation auf Ihrem Testgerät zu deaktivieren, gehen Sie zu die Einstellungen > Entwickleroptionen und deaktivieren Sie alle folgenden Optionen im Abschnitt "Zeichnen":
Zuerst testen wir einen Login-Bildschirm. Der Anmeldevorgang beginnt wie folgt: Der Benutzer startet die App und der erste Bildschirm enthält einen Bildschirm Anmeldung Taste. Wenn das Anmeldung Schaltfläche angeklickt wird, öffnet sich die LoginActivity
Bildschirm. Dieser Bildschirm enthält nur zwei Text bearbeiten
s (die Felder Benutzername und Passwort) und a einreichen Taste.
Hier ist was unser Hauptaktivität
Layout sieht so aus:
Hier ist was unser LoginActivity
Layout sieht so aus:
Schreiben wir jetzt einen Test für unsere Hauptaktivität
Klasse. Gehen Sie zu Ihrem Hauptaktivität
bewegen Sie den Cursor auf die Hauptaktivität
Name und drücken Sie Shift-Control-T. Wählen Neuen Test erstellen… im Popup-Menü.
Drücken Sie die OK und ein weiterer Dialog erscheint. Wählen Sie das androidTest Verzeichnis und klicken Sie auf die OK Taste noch einmal. Beachten Sie, dass sich die Testfälle in dem Test befinden, da wir einen Instrumentationstest (Android SDK-spezifische Tests) schreiben androidTest / Java Mappe.
Nun hat Android Studio erfolgreich eine Testklasse für uns erstellt. Fügen Sie über dem Klassennamen die folgende Anmerkung ein: @RunWith (AndroidJUnit4 :: class)
.
import android.support.test.runner.AndroidJUnit4 import org.junit.runner.RunWith @RunWith (AndroidJUnit4 :: class) -Klasse MainActivityTest
Diese Anmerkung bedeutet, dass alle Tests in dieser Klasse Android-spezifische Tests sind.
Da wir eine Aktivität testen möchten, müssen wir ein paar Einstellungen vornehmen. Wir müssen Espresso darüber informieren, welche Aktivität geöffnet oder gestartet werden muss, bevor eine Testmethode ausgeführt und gelöscht wird.
import android.support.test.rule.ActivityTestRule import android.support.test.runner.AndroidJUnit4 import org.junit.Rule import org.junit.runner.Runner.RunWith @RunWith (AndroidJUnit4 :: class) Klasse MainActivityTest @Rule @JvFF activityRule = ActivityTestRule(MainActivity :: class.java)
Notiere dass der @Regel
Anmerkung bedeutet, dass es sich um eine JUnit4-Testregel handelt. JUnit4-Testregeln werden vor und nach jeder Testmethode ausgeführt (kommentiert mit @Prüfung
). In unserem eigenen Szenario wollen wir starten Hauptaktivität
vor jeder Testmethode und zerstöre es danach.
Wir haben auch die aufgenommen @JvmField
Kotlin-Anmerkung Dadurch wird der Compiler lediglich angewiesen, keine Getter und Setter für die Eigenschaft zu generieren, sondern diese als einfaches Java-Feld bereitzustellen.
Hier sind die drei Hauptschritte beim Schreiben eines Espresso-Tests:
Textvorschau
oder Taste
) Sie möchten testen.Die folgenden Arten von Anmerkungen können auf die innerhalb der Testklasse verwendeten Methoden angewendet werden.
@Vor dem Unterricht
: Dies zeigt an, dass die statische Methode, auf die diese Anmerkung angewendet wird, einmal und vor allen Tests in der Klasse ausgeführt werden muss. Dies kann zum Beispiel verwendet werden, um eine Verbindung zu einer Datenbank herzustellen. @Vor
: Gibt an, dass die Methode, an die diese Anmerkung angehängt ist, vor jeder Testmethode in der Klasse ausgeführt werden muss.@Prüfung
: gibt an, dass die Methode, an die diese Anmerkung angehängt ist, als Testfall ausgeführt werden soll.@Nach dem
: Gibt an, dass die Methode, an die diese Anmerkung angehängt ist, nach jeder Testmethode ausgeführt werden soll. @Nach dem Unterricht
: Gibt an, dass die Methode, an die diese Anmerkung angehängt ist, ausgeführt werden soll, nachdem alle Testmethoden in der Klasse ausgeführt wurden. Hier schließen wir normalerweise Ressourcen aus, die in geöffnet wurden @Vor dem Unterricht
. Aussicht
Verwenden onView ()
In unserer Hauptaktivität
Layout-Datei, wir haben nur ein Widget Anmeldung
Taste. Lassen Sie uns ein Szenario testen, in dem ein Benutzer diese Schaltfläche findet und darauf klickt.
import android.support.test.espresso.Espresso.onView import android.support.test.espresso.matcher.ViewMatchers.withId //… @RunWith (AndroidJUnit4 :: class) Klasse MainActivityTest //… @Test @Throws (Ausnahme: : class) fun clickLoginButton_opensLoginUi () onView (withId (R.id.btn_login))
Um Widgets in Espresso zu finden, verwenden wir die onView ()
statische Methode (statt findViewById ()
). Der Parametertyp, den wir liefern onView ()
ist ein Matcher
. Notiere dass der Matcher
Die API stammt nicht aus dem Android SDK, sondern aus dem Hamcrest-Projekt. Hamcrests Matcher-Bibliothek befindet sich in der Espresso-Bibliothek, die wir über Gradle gezogen haben.
Das onView (withId (R.id.btn_login))
wird ein zurückkehren ViewInteraction
das ist für a Aussicht
deren ID ist R.id.btn_login
. Im obigen Beispiel haben wir verwendet withId ()
ein Widget mit einer angegebenen ID suchen. Andere View Matchers, die wir verwenden können, sind:
withText ()
: gibt einen passenden Matcher zurück Textvorschau
basierend auf seinem Texteigenschaftswert.withHint ()
: gibt einen passenden Matcher zurück Textvorschau
basierend auf seinem Hinweis-Eigenschaftswert.withTagKey ()
: gibt einen passenden Matcher zurück Aussicht
basierend auf Tag-Schlüsseln.withTagValue ()
: gibt einen passenden Matcher zurück Aussicht
s basiert auf Tag-Eigenschaftswerten.Lassen Sie uns zunächst testen, ob die Schaltfläche tatsächlich auf dem Bildschirm angezeigt wird.
onView (withId (R.id.btn_login)). check (match (isDisplayed ()))
Hier bestätigen wir nur, ob der Button mit der angegebenen ID (R.id.btn_login
) ist für den Benutzer sichtbar, also verwenden wir die prüfen()
Methode, um zu überprüfen, ob der Basiswert Aussicht
hat einen bestimmten Zustand - in unserem Fall, wenn es sichtbar ist.
Das Streichhölzer()
statische Methode gibt ein generisches zurück ViewAssertion
Dadurch wird behauptet, dass eine Ansicht in der Ansichtshierarchie vorhanden ist und vom angegebenen Ansichtsabgleicher abgeglichen wird. Der angegebene View-Matcher wird beim Aufruf zurückgegeben wird angezeigt()
. Wie durch den Methodennamen vorgeschlagen, wird angezeigt()
ist ein Matcher, der passt Aussicht
s, die dem Benutzer aktuell auf dem Bildschirm angezeigt werden. Wenn wir beispielsweise prüfen möchten, ob eine Schaltfläche aktiviert ist, übergeben wir einfach aktiviert()
zu Streichhölzer()
.
Andere beliebte View Matchers können wir in die Streichhölzer()
Methode sind:
hasFocus ()
: gibt einen passenden Matcher zurück Aussicht
s, die aktuell fokussiert sind.wird geprüft()
: Gibt einen Matcher zurück, der nur dann akzeptiert, wenn die Ansicht a ist CompoundButton
(oder Untertyp von) und befindet sich im geprüften Zustand. Das Gegenteil dieser Methode ist isNotChecked ()
. ist ausgewählt()
: gibt einen passenden Matcher zurück Aussicht
s, die ausgewählt werden.Um den Test auszuführen, können Sie auf das grüne Dreieck neben der Methode oder auf den Klassennamen klicken. Wenn Sie auf das grüne Dreieck neben dem Klassennamen klicken, werden alle Testmethoden in dieser Klasse ausgeführt. Mit der Methode neben einer Methode wird der Test nur für diese Methode ausgeführt.
Hurra! Unser Test ist bestanden!
Auf einen ViewInteraction
Objekt, das beim Aufruf zurückgegeben wird onView ()
, Wir können Aktionen simulieren, die ein Benutzer mit einem Widget ausführen kann. Zum Beispiel können wir eine Klickaktion simulieren, indem Sie einfach die klicken()
statische Methode innerhalb der ViewActions
Klasse. Dies wird eine zurückgeben ViewAction
Objekt für uns.
Die Dokumentation sagt das ViewAction
ist:
Verantwortlich für die Durchführung einer Interaktion mit dem angegebenen View-Element.
@Testspaß clickLoginButton_opensLoginUi () //… onView (withId (R.id.btn_login)). Perform (click ())
Wir führen ein Klickereignis durch, indem wir zuerst anrufen ausführen()
. Diese Methode führt die angegebenen Aktionen für die Ansicht aus, die vom aktuellen Ansichtsabgleicher ausgewählt wurde. Beachten Sie, dass wir eine einzelne Aktion oder eine Liste von Aktionen (in Reihenfolge ausgeführt) übergeben können. Hier haben wir es gegeben klicken()
. Andere mögliche Aktionen sind:
Text eingeben()
Texteingabe in eine Text bearbeiten
.Klartext()
Klartext in einem simulieren Text bearbeiten
.Doppelklick()
Doppelklicken auf a Aussicht
.longClick ()
langes Klicken imitieren a Aussicht
.scrollTo ()
simulieren Sie das Scrollen a ScrollView
zu einem bestimmten Aussicht
das ist sichtbar. nach links wischen()
simulieren Sie das Wischen von rechts nach links über die vertikale Mitte von a Aussicht
.Viele weitere Simulationen finden Sie im ViewActions
Klasse.
Lassen Sie uns unseren Test abschließen, um das zu bestätigen LoginActivity
Der Bildschirm wird immer angezeigt, wenn die Anmeldung Schaltfläche ist angeklickt. Obwohl wir bereits gesehen haben, wie man es benutzt prüfen()
auf einen ViewInteraction
, lass es uns nochmal benutzen, noch ein anderes ViewAssertion
.
@Testspaß clickLoginButton_opensLoginUi () //… onView (withId (R.id.tv_login)). Check (entspricht (isDisplayed ()))
In der LoginActivity
Layoutdatei, abgesehen von Text bearbeiten
s und a Taste
, wir haben auch eine Textvorschau
mit ID R.id.tv_login
. Also prüfen wir einfach, ob die Textvorschau
ist für den Benutzer sichtbar.
Jetzt können Sie den Test erneut ausführen!
Ihre Tests sollten erfolgreich bestanden werden, wenn Sie alle Schritte korrekt ausgeführt haben.
Folgendes ist während der Ausführung unserer Tests passiert:
Hauptaktivität
Verwendung der Aktivitätsregel
Feld.R.id.btn_login
war sichtbar (wird angezeigt()
) an den Benutzer.klicken()
) auf dieser Schaltfläche.LoginActivity
wurde dem Benutzer angezeigt, indem geprüft wurde, ob a Textvorschau
mit id R.id.tv_login
in dem LoginActivity
ist sichtbar.Sie können immer auf das Espresso-Spickzettel zugreifen, um die verschiedenen Ansichtsabgleichungen, Ansichtsaktionen und Ansichtsassertionen anzuzeigen.
LoginActivity
BildschirmHier ist unser LoginActivity.kt:
import android.os.Bundle import android.support.v7.app.AppCompatActivity import android.widget.Button import android.widget.EditText import android.widget.TextView-Klasse LoginActivity: AppCompatActivity () private lateinit var usernameEditText: EditText private lateinit var loginTitleTextView: TextView privat lateinit var passwordEditText: EditText privat lateinit var submitButton: Schaltfläche überschreiben fun onCreate (savedInstanceState: Bundle?) super.onCreate (savedInstanceState) setContentView (R.layout.activity_login) = findViewById (R.id.et_password) submitButton = findViewById (R.id.btn_submit) loginTitleTextView = findViewById (R.id.tv_login) submitButton.setOnClickListener if (usernameEditText.text.totring () =). text.toString () == "password") loginTitleTextView.text = "Erfolg" else loginTitleTextView.text = "Failure"
Wenn im obigen Code der eingegebene Benutzername "chike" und das Kennwort "password" lautet, ist die Anmeldung erfolgreich. Bei allen anderen Eingaben ist dies ein Fehler. Lassen Sie uns jetzt einen Espresso-Test dafür schreiben!
Gehe zu LoginActivity.kt, Bewegen Sie den Cursor auf die LoginActivity
Name und drücken Sie Shift-Control-T. Wählen Neuen Test erstellen… im Popup-Menü. Folgen Sie dem gleichen Vorgang wie für uns MainActivity.kt, und klicken Sie auf die OK Taste.
import android.support.test.espresso.Espresso import android.support.test.espresso.Espresso.onView import android.support.test.espresso.action.ViewActions import android.support.test.espresso.assertion.ViewAssertions.matches importieren android .support.test.espresso.matcher.ViewMatchers.withId import android.support.test.espresso.matcher.ViewMatchers.withText import android.support.test.rule.ActivityTestRule import android.support.test.runner.AndroidJUnit4 import org.junit .Rule import org.junit.Test import org.junit.runner.RunWith @RunWith (AndroidJUnit4 :: class) Klasse LoginActivityTest @Rule @JvmField var activityRule = ActivityTestRule(LoginActivity :: class.java) private val username = "chike" privates val password = "password" @Test fun clickLoginButton_opensLoginUi () onView (withId (R.id.et_username)). Perform (ViewActions.typeText (username)) onView (withId (R.id.et_password)). perform (ViewActions.typeText (password)) onView (withId (R.id.btn_submit)). perform (ViewActions.scrollTo (), ViewActions.click ()) Espresso.onView (withId (R.id.tv_login)) .check (passt (withText ("Success")))
Diese Testklasse ist unserer ersten sehr ähnlich. Wenn wir den Test durchführen, unser LoginActivity
Bildschirm wird geöffnet. Der Benutzername und das Passwort werden in eingegeben R.id.et_username
und R.id.et_password
jeweils Felder. Als Nächstes klicken Sie auf Espresso einreichen Taste (R.id.btn_submit
). Es wird warten bis a Aussicht
mit id R.id.tv_login
kann mit Textlesung gefunden werden Erfolg.
RecyclerView
RecyclerViewActions
ist die Klasse, die eine Reihe von APIs zur Verfügung stellt, um mit a zu arbeiten RecyclerView
. RecyclerViewActions
ist Teil eines separaten Artefakts im Espresso-Beitrag
Artefakt, das auch hinzugefügt werden sollte build.gradle:
androidTestImplementation 'com.android.support.test.espresso: espresso-contrib: 3.0.2'
Beachten Sie, dass dieses Artefakt auch die API für die Benutzeroberfläche enthält, die das Navigationsfach durch testet Schubladenaktionen
und DrawerMatchers
.
@RunWith (AndroidJUnit4 :: class) -Klasse MyListActivityTest //… @Test fun clickItem () onView (withId (R.id.rv)) .perform (RecyclerViewActions .actionOnItemAtPosition.)(0, ViewActions.click ()))
Klicken Sie auf ein Element an einer beliebigen Position in a RecyclerView
, wir rufen an actionOnItemAtPosition ()
. Wir müssen ihm einen Gegenstand geben. In unserem Fall ist der Artikel der ViewHolder
Klasse in unserem Zufallsadapter
. Diese Methode nimmt auch zwei Parameter auf. Die erste ist die Position und die zweite ist die Aktion (ViewActions.click ()
).
Andere RecyclerViewActions
das durchgeführt werden kann sind:
actionOnHolderItem ()
: führt ein ViewAction
in einer Ansicht von viewHolderMatcher
. Dies ermöglicht es uns, es durch das, was in dem enthalten ist, abzugleichen ViewHolder
und nicht die Position. scrollToPosition ()
: gibt a zurück ViewAction
welche scrollt RecyclerView
zu einer Position.Als nächstes (sobald der Bildschirm "Notiz hinzufügen" geöffnet ist) geben wir unseren Notiztext ein und speichern die Notiz. Wir müssen nicht warten, bis der neue Bildschirm geöffnet wird - Espresso erledigt dies automatisch für uns. Sie wartet bis zu einem View mit der ID R.id.add_note_title
kann gefunden werden.
Espresso verwendet ein anderes Artefakt namens Espresso-Absichten
zum Testen von Absichten. Dieses Artefakt ist nur eine weitere Erweiterung von Espresso, die sich auf die Validierung und Verspottung von Absichten konzentriert. Schauen wir uns ein Beispiel an.
Zuerst müssen wir die ziehen Espresso-Absichten
Bibliothek in unser Projekt.
androidTestImplementation 'com.android.support.test.espresso: espresso-Absichten: 3.0.2'
import android.support.test.espresso.intent.rule.IntentsTestRule import android.support.test.runner.AndroidJUnit4 import org.junit.Rule import org.junit.runner.RunWith @RunWith (AndroidJUnit4 :: class) Klasse PickContactActivityTest Regel @JvmField var intentRule = IntentsTestRule(PickContactActivity :: class.java)
IntentsTestRule
erweitert ActivityTestRule
, also haben sie beide ein ähnliches Verhalten. Folgendes sagt der Doc:
Diese Klasse ist eine Erweiterung vonActivityTestRule
, welches die Espresso-Intents vor jedem mit Anmerkungen versehenen Test initialisiertPrüfung
und gibt nach jedem Testlauf Espresso-Intents frei. Die Aktivität wird nach jedem Test beendet und diese Regel kann auf dieselbe Weise wie verwendet werdenActivityTestRule
.
Das Hauptunterscheidungsmerkmal ist, dass es zusätzliche Funktionen zum Testen bietet startActivity ()
und startActivityForResult ()
mit Mock und Stubs.
Wir werden jetzt ein Szenario testen, in dem ein Benutzer auf eine Schaltfläche klickt (R.id.btn_select_contact
) auf dem Bildschirm, um einen Kontakt aus der Kontaktliste des Telefons auszuwählen.
//… @Test fun stubPick () var result = Instrumentation.ActivityResult (Activity.RESULT_OK, Intent (null, ContactsContract.Contacts.CONTENT_URI)) intending (hasAction (Intent.ACTION_PICK)). R.id.btn_select_contact)). Perform (click ()) bestimmt (allOf (toPackage ("com.google.android.contacts"), hasAction (Intent.ACTION_PICK), hasData (ContactsContract.Contacts.CONTENT_URI)) …
Hier benutzen wir beabsichtigen ()
von dem Espresso-Absichten
Bibliothek, um einen Stub mit einer Scheinreaktion für unsere zu erstellen ACTION_PICK
anfordern. Hier passiert was in PickContactActivity.kt Wenn der Benutzer auf die Schaltfläche mit der ID klickt R.id.btn_select_contact
einen Kontakt auswählen.
fun pickContact (v: View) val i = Intent (Intent.ACTION_PICK, ContactsContracts.Contacts.CONTENT_URI) startActivityForResult (i, PICK_REQUEST)
beabsichtigen ()
nimmt ein Matcher
das mit den Absichten übereinstimmt, für die eine abgestumpfte Antwort bereitgestellt werden sollte. Mit anderen Worten, die Matcher
gibt an, für welche Anfrage Sie sich interessieren. In unserem eigenen Fall machen wir davon Gebrauch hasAction ()
(eine Hilfsmethode in IntentMatchers
) um unsere zu finden ACTION_PICK
anfordern. Wir rufen dann an Antworten mit()
, das setzt das Ergebnis für onActivityResult ()
. In unserem Fall hat das Ergebnis ergeben Aktivität.RESULT_OK
, Simulieren des Benutzers, indem Sie einen Kontakt aus der Liste auswählen.
Wir simulieren dann, indem Sie auf die Schaltfläche Kontakt auswählen klicken, die anruft startActivityForResult ()
. Beachten Sie, dass unser Stub die Scheinantwort an gesendet hat onActivityResult ()
.
Zum Schluss verwenden wir die beabsichtigt()
Helfer-Methode, um einfach zu überprüfen, dass die Aufrufe an startActivity ()
und startActivityForResult ()
wurden mit den richtigen Informationen gemacht.
In diesem Lernprogramm haben Sie gelernt, wie Sie das Espresso-Testframework in Ihrem Android Studio-Projekt auf einfache Weise verwenden können, um Ihren Testworkflow zu automatisieren.
Es wird dringend empfohlen, die offizielle Dokumentation zu lesen, um mehr über das Schreiben von UI-Tests mit Espresso zu erfahren.