Espressogrundlagen

In diesem Dokument wird erläutert, wie Sie häufige automatisierte Testaufgaben mithilfe der Espresso API

Die Espresso API regt Testautoren dazu an, darüber nachzudenken, was Nutzer bei der Interaktion mit der Anwendung – UI-Elemente finden und interagieren mit ihnen teilen. Gleichzeitig verhindert das Framework den direkten Zugriff auf Aktivitäten. und Ansichten der Anwendung erstellen, da die Nutzung dieser Objekte außerhalb des UI-Threads ist eine Hauptursache für instabile Tests. Daher werden Sie Methoden wie getView() und getCurrentActivity() in der Espresso API nicht sehen. Sie können weiterhin sicher mit Ansichten arbeiten, indem Sie eigene abgeleitete Klassen von ViewAction und ViewAssertion.

API-Komponenten

Zu den Hauptkomponenten von Espresso gehören:

  • Espresso – Einstiegspunkt für Interaktionen mit Aufrufen (über onView() und onData(). Außerdem werden APIs zur Verfügung gestellt, die nicht unbedingt an eine Ansicht gebunden sind, wie als pressBack().
  • ViewMatchers – Eine Sammlung von Objekten, die die Klasse Matcher<? super View>-Schnittstelle. Sie können eine oder mehrere onView()-Methode zum Suchen einer Ansicht in der aktuellen Ansichtshierarchie.
  • ViewActions: Eine Sammlung von ViewAction-Objekten, die an die ViewInteraction.perform()-Methode, z. B. click().
  • ViewAssertions: Eine Sammlung von ViewAssertion-Objekten, die hat die Methode ViewInteraction.check() übergeben. Meistens verwenden Sie stimmt mit Assertion überein. Dabei wird ein View-Matcher verwendet, um den Status des aktuell ausgewählte Ansicht.

Beispiel:

Kotlin

// withId(R.id.my_view) is a ViewMatcher
// click() is a ViewAction
// matches(isDisplayed()) is a ViewAssertion
onView(withId(R.id.my_view))
    .perform(click())
    .check(matches(isDisplayed()))

Java

// withId(R.id.my_view) is a ViewMatcher
// click() is a ViewAction
// matches(isDisplayed()) is a ViewAssertion
onView(withId(R.id.my_view))
    .perform(click())
    .check(matches(isDisplayed()));

Ansicht suchen

In den meisten Fällen wird für die onView()-Methode ein Hamcrest-Matcher verwendet. die nur mit genau einer Datenansicht in der aktuellen Ansicht übereinstimmen muss. Hierarchie. Matcher sind leistungsstark und kennen diejenigen, mit Mockito oder JUnit. Wenn Sie mit Hamcrest-Matchern nicht vertraut sind, sollten Sie sich zunächst diese Präsentation.

Häufig hat die gewünschte Ansicht eine eindeutige R.id und einen einfachen withId-Matcher die Suche eingrenzen. Es gibt jedoch viele seriöse Fälle, in denen Sie R.id kann bei der Testentwicklungszeit nicht ermittelt werden. Zum Beispiel kann die spezifische Ansicht hat möglicherweise kein R.id oder das R.id ist nicht eindeutig. Das kann sich normal Instrumentierungstests anfällig und kompliziert zu schreiben, da der normale Weg Zugriff auf die Ansicht – mit findViewById() – funktioniert nicht. Daher können Sie auf private Mitglieder der Aktivität oder des Fragments zugreifen müssen, nach einem Container mit einem bekannten R.id suchen und zum Inhalt für den für eine bestimmte Ansicht.

Espresso bewältigt dieses Problem sauber, indem er Ihnen ermöglicht, die Ansicht einzugrenzen. Sie können entweder vorhandene ViewMatcher-Objekte oder eigene benutzerdefinierte Objekte verwenden.

Um eine Ansicht anhand ihrer R.id zu finden, müssen Sie nur onView() aufrufen:

Kotlin

onView(withId(R.id.my_view))

Java

onView(withId(R.id.my_view));

Manchmal werden R.id-Werte von mehreren Datenansichten gemeinsam verwendet. In diesem Fall Wenn Sie versuchen, ein bestimmtes R.id-Objekt zu verwenden, erhalten Sie eine Ausnahme, z. B. AmbiguousViewMatcherException. In der Ausnahmemeldung wird eine Textnachricht Darstellung der aktuellen Ansichtshierarchie, nach der Sie suchen und die Ansichten, die mit dem nicht eindeutigen R.id übereinstimmen:

java.lang.RuntimeException:
androidx.test.espresso.AmbiguousViewMatcherException
This matcher matches multiple views in the hierarchy: (withId: is <123456789>)

...

+----->SomeView{id=123456789, res-name=plus_one_standard_ann_button,
visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true,
window-focus=true, is-focused=false, is-focusable=false, enabled=true,
selected=false, is-layout-requested=false, text=,
root-is-layout-requested=false, x=0.0, y=625.0, child-count=1}
****MATCHES****
|
+------>OtherView{id=123456789, res-name=plus_one_standard_ann_button,
visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true,
window-focus=true, is-focused=false, is-focusable=true, enabled=true,
selected=false, is-layout-requested=false, text=Hello!,
root-is-layout-requested=false, x=0.0, y=0.0, child-count=1}
****MATCHES****

Wenn Sie die verschiedenen Attribute der Ansichten betrachten, identifizierbaren Eigenschaften. Im obigen Beispiel enthält eine der Ansichten den Text "Hello!" Mit dieser Kombination können Sie Ihre Suche eingrenzen, Matcher:

Kotlin

onView(allOf(withId(R.id.my_view), withText("Hello!")))

Java

onView(allOf(withId(R.id.my_view), withText("Hello!")));

Sie können auch festlegen, dass kein Abgleich rückgängig gemacht werden soll:

Kotlin

onView(allOf(withId(R.id.my_view), not(withText("Unwanted"))))

Java

onView(allOf(withId(R.id.my_view), not(withText("Unwanted"))));

Weitere Informationen finden Sie unter ViewMatchers. für die von Espresso bereitgestellten Tool zum Abgleich von Ansichten.

Wissenswertes

  • Alle Ansichten, mit denen ein Nutzer interagieren kann, in einer gut funktionierenden App. sollte entweder beschreibenden Text oder eine Inhaltsbeschreibung enthalten. Weitere Informationen finden Sie unter Apps barrierefrei gestalten Details. Wenn Sie die Suche nicht mit withText() eingrenzen können oder withContentDescription(), sollten Sie dies als Programmfehler in Bezug auf die Barrierefreiheit behandeln.
  • Den am wenigsten beschreibenden Matcher verwenden, mit dem die gesuchte Ansicht gefunden wird für die Sie angegeben haben. Geben Sie nicht zu viele Details an, da das Framework dann zwingt, mehr Arbeit zu erledigen als ist notwendig. Ist eine Ansicht beispielsweise anhand ihres Textes eindeutig identifizierbar, geben Sie Sie müssen nicht angeben, dass die Ansicht auch über TextView zuweisbar ist. Viele Aufrufe, sollte die R.id des Aufrufs ausreichen.
  • Befindet sich die Zielansicht in einem AdapterView, z. B. ListView, GridView oder Spinner: Die Methode onView() funktioniert möglicherweise nicht. In diesen Cases verwenden, sollten Sie stattdessen onData() verwenden.

Aktion für eine Datenansicht ausführen

Wenn Sie einen passenden Abgleicher für die Zielansicht gefunden haben, führen Sie mit der Perform-Methode Instanzen von ViewAction dafür aus.

So klicken Sie beispielsweise auf die Ansicht:

Kotlin

onView(...).perform(click())

Java

onView(...).perform(click());

Mit einem „Perform Call“ können Sie mehrere Aktionen ausführen:

Kotlin

onView(...).perform(typeText("Hello"), click())

Java

onView(...).perform(typeText("Hello"), click());

Wenn sich die Ansicht, mit der Sie arbeiten, in einem ScrollView (vertikal oder horizontal), erwägen Sie die vorherigen Aktionen, für die die Ansicht angezeigt werden, z. B. click() und typeText(), mit scrollTo(). Dieses stellt sicher, dass die Ansicht angezeigt wird, bevor mit der anderen Aktion fortgefahren wird:

Kotlin

onView(...).perform(scrollTo(), click())

Java

onView(...).perform(scrollTo(), click());

Weitere Informationen finden Sie unter ViewActions. für die Ansichtsaktionen von Espresso.

Assertions ansehen

Assertions können mit der check() auf die aktuell ausgewählte Ansicht angewendet werden . Die am häufigsten verwendete Assertion ist die Assertion matches(). Dabei wird ein ViewMatcher-Objekt, um den Status der aktuell ausgewählten Ansicht zu bestätigen.

So prüfen Sie beispielsweise, ob eine Ansicht den Text "Hello!" enthält:

Kotlin

onView(...).check(matches(withText("Hello!")))

Java

onView(...).check(matches(withText("Hello!")));

Wenn Sie bestätigen möchten, dass "Hello!" Inhalt der Ansicht ist, gilt Folgendes als nicht empfehlenswert:

Kotlin

// Don't use assertions like withText inside onView.
onView(allOf(withId(...), withText("Hello!"))).check(matches(isDisplayed()))

Java

// Don't use assertions like withText inside onView.
onView(allOf(withId(...), withText("Hello!"))).check(matches(isDisplayed()));

Wenn Sie hingegen bestätigen möchten, dass eine Ansicht mit dem Text "Hello!" z. B. nach einer Änderung der Kennzeichnung für die Sichtbarkeit von Aufrufen, ist in Ordnung.

Einfacher Assertion-Test ansehen

In diesem Beispiel enthält SimpleActivity einen Button und einen TextView. Wenn der Parameter angeklickt wird, ändert sich der Inhalt von TextView in "Hello Espresso!".

So testen Sie dies mit Espresso:

Klicken Sie auf die Schaltfläche.

Der erste Schritt besteht darin, nach einer Eigenschaft zu suchen, die dabei hilft, die Schaltfläche zu finden. Die Schaltfläche im SimpleActivity hat wie erwartet eine eindeutige R.id.

Kotlin

onView(withId(R.id.button_simple))

Java

onView(withId(R.id.button_simple));

So führen Sie den Klick aus:

Kotlin

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

Java

onView(withId(R.id.button_simple)).perform(click());

TextView-Text überprüfen

Die TextView mit dem zu bestätigenden Text hat auch eine eindeutige R.id:

Kotlin

onView(withId(R.id.text_simple))

Java

onView(withId(R.id.text_simple));

So überprüfen Sie den Inhaltstext:

Kotlin

onView(withId(R.id.text_simple)).check(matches(withText("Hello Espresso!")))

Java

onView(withId(R.id.text_simple)).check(matches(withText("Hello Espresso!")));

Laden von Daten in Adapteransichten prüfen

AdapterView ist eine spezielle Art von Widget, das Daten dynamisch aus einen Adapter. Das häufigste Beispiel für ein AdapterView ist ListView. Als statische Widgets wie LinearLayout dagegen nur einen Teil der AdapterView untergeordnete Elemente können in die aktuelle Ansichtshierarchie geladen werden. Eine einfache Bei der Suche mit onView() wurden keine Ansichten gefunden, die momentan nicht geladen sind.

Espresso verarbeitet dies durch die Bereitstellung eines separaten onData()-Einstiegspunkts, das betreffende Adapterelement zuerst laden und vor der oder einem seiner untergeordneten Elemente.

Warnung: Benutzerdefinierte Implementierungen von AdapterView kann Probleme mit onData() haben wenn sie Vererbungsverträge brechen, getItem()-API. In diesen Fällen ist es am besten, Ihren Anwendungscode refaktorieren. Wenn dies nicht möglich ist, können Sie Übereinstimmung mit dem benutzerdefinierten AdapterViewProtocol. Weitere Informationen erhalten Sie in seht euch die Standardeinstellungen an <ph type="x-smartling-placeholder"></ph> Die Klasse AdapterViewProtocols wird von Espresso bereitgestellt.

Einfacher Test der Adapteransicht

Dieser einfache Test zeigt, wie onData() verwendet wird. SimpleActivity enthält einen Spinner mit einigen Elementen, die verschiedene Kaffeegetränke repräsentieren. Wenn ein ausgewählt ist, gibt es einen TextView, der sich in "One %s a day!" ändert, wobei %s steht für das ausgewählte Element.

Das Ziel dieses Tests ist es, die Spinner zu öffnen, ein bestimmtes Element auszuwählen und Prüfen Sie, ob der TextView das Element enthält. Da die Spinner-Klasse auf Am AdapterView wird empfohlen, onData() statt onView() für mit dem Artikel übereinstimmen.

Elementauswahl öffnen

Kotlin

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

Java

onView(withId(R.id.spinner_simple)).perform(click());

Element auswählen

Für die Elementauswahl erstellt Spinner eine ListView mit ihrem Inhalt. Diese Ansicht kann sehr lang sein und das Element wird möglicherweise nicht zur Ansicht beigetragen Hierarchie. Mit onData() erzwingen wir das gewünschte Element in der Ansicht Hierarchie. Die Elemente im Spinner sind Strings, also möchten wir ein Element abgleichen. der dem String "Americano" entspricht:

Kotlin

onData(allOf(`is`(instanceOf(String::class.java)),
        `is`("Americano"))).perform(click())

Java

onData(allOf(is(instanceOf(String.class)), is("Americano"))).perform(click());

Überprüfe, ob der Text korrekt ist

Kotlin

onView(withId(R.id.spinnertext_simple))
    .check(matches(withText(containsString("Americano"))))

Java

onView(withId(R.id.spinnertext_simple))
    .check(matches(withText(containsString("Americano"))));

Fehlerbehebung

Espresso bietet nützliche Informationen zur Fehlerbehebung, wenn ein Test fehlschlägt:

Protokollierung

Espresso protokolliert alle Ansichtsaktionen an logcat. Beispiel:

ViewInteraction: Performing 'single click' action on view with text: Espresso

Hierarchie ansehen

Espresso druckt die Ansichtshierarchie in der Ausnahmemeldung aus, wenn onView() schlägt fehl.

  • Wenn onView() die Zielansicht nicht findet, wird ein NoMatchingViewException geworfen werden. Sie können die Ansichtshierarchie im Ausnahmestring überprüfen, um die Ansicht zu analysieren warum für den Matcher keine Übereinstimmung gefunden wurde.
  • Wenn onView() mehrere Ansichten findet, die mit dem angegebenen Matcher übereinstimmen, wird ein AmbiguousViewMatcherException wird geworfen. Die Ansichtshierarchie wird gedruckt und alle übereinstimmende Ansichten sind mit dem Label MATCHES gekennzeichnet:
java.lang.RuntimeException:
androidx.test.espresso.AmbiguousViewMatcherException
This matcher matches multiple views in the hierarchy: (withId: is <123456789>)

...

+----->SomeView{id=123456789, res-name=plus_one_standard_ann_button,
visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true,
window-focus=true, is-focused=false, is-focusable=false, enabled=true,
selected=false, is-layout-requested=false, text=,
root-is-layout-requested=false, x=0.0, y=625.0, child-count=1}
****MATCHES****
|
+------>OtherView{id=123456789, res-name=plus_one_standard_ann_button,
visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true,
window-focus=true, is-focused=false, is-focusable=true, enabled=true,
selected=false, is-layout-requested=false, text=Hello!,
root-is-layout-requested=false, x=0.0, y=0.0, child-count=1}
****MATCHES****

Bei einer komplizierten Ansichtshierarchie oder einem unerwarteten Verhalten von Widgets ist es immer hilfreich, die Funktion Hierarchy Viewer in Android Studio für eine Erklärung.

Warnungen bei der Adapteransicht

Espresso warnt Nutzer vor AdapterView-Widgets. Wenn ein onView() löst einen NoMatchingViewException-Vorgang aus und AdapterView-Widgets werden in der Ansichtshierarchie ist die häufigste Lösung die Verwendung von onData(). Die Ausnahmemeldung enthält eine Warnung mit einer Liste der Adapteransichten. Mit diesen Informationen können Sie onData() aufrufen, um die Zielansicht zu laden.

Weitere Informationen

Weitere Informationen zur Verwendung von Espresso in Android-Tests finden Sie in der in den folgenden Ressourcen.

Produktproben