Android
Testen der Benutzeroberfläche mit Espresso
Suche…
Bemerkungen
Espresso
Espresso Spickzettel hilft Ihnen, Ihre Tests zu schreiben und was Sie testen möchten:
https://google.github.io/android-testing-support-library/docs/espresso/cheatsheet/
Ein guter Bezugspunkt ist auch immer die offizielle Dokumentation:
https://google.github.io/android-testing-support-library/docs/espresso/index.html
Fortgeschrittene Espresso-Video-Vorschläge von Google: https://www.youtube.com/watch?v=isihPOY2vS4
Fehlerbehebung
- Schließen Sie beim Scrollen unbedingt die Tastatur:
Achtung: Wenn Sie die "Espresso" -Version nicht verwenden, können Sie nichts tun, wenn Sie sie außerhalb einer ViewAction verwenden. Dies ist möglicherweise nicht offensichtlich, wenn Sie einen Import in der ViewAction-Version haben, da sie exakt denselben Methodennamen haben.
ViewActions.closeSoftKeyboard;
Espresso.closeSoftKeyboard();
- Wenn Sie Tests in einer Suite statt einzeln ausführen, beachten Sie, dass die Aktivität des vorherigen Tests möglicherweise noch ausgeführt wird. Verlassen Sie sich nicht darauf, dass onDestroy () des vorherigen Tests vor den aktuellen Tests onResume () aufgerufen wird. Es stellt sich heraus, dass dies tatsächlich ein Fehler ist : http://b.android.com/201513
Espresso einrichten
build.gradle
Sie in der build.gradle
Datei Ihres Android-App-Moduls die nächsten Abhängigkeiten hinzu:
dependencies {
// Android JUnit Runner
androidTestCompile 'com.android.support.test:runner:0.5'
// JUnit4 Rules
androidTestCompile 'com.android.support.test:rules:0.5'
// Espresso core
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
// Espresso-contrib for DatePicker, RecyclerView, Drawer actions, Accessibility checks, CountingIdlingResource
androidTestCompile 'com.android.support.test.espresso:espresso-contrib:2.2.2'
//UI Automator tests
androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.2.2'
}
AndroidJUnitRunner
den AndroidJUnitRunner
für den Parameter testInstrumentationRunner
in der Datei build.gradle
.
android {
defaultConfig {
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
}
Fügen Sie außerdem diese Abhängigkeit hinzu, um Unterstützung für Vorspiegelungen bereitzustellen
androidTestCompile 'com.android.support.test.espresso:espresso-intents:2.2.2'
Fügen Sie diese Option für die Unterstützung von Webview-Tests hinzu
// Espresso-web for WebView support
androidTestCompile 'com.android.support.test.espresso:espresso-web:2.2.2'
Erstellen Sie eine Espresso-Testklasse
Legen Sie die nächste Java-Klasse in src / androidTest / java ab und führen Sie sie aus.
public class UITest {
@Test public void Simple_Test() {
onView(withId(R.id.my_view)) // withId(R.id.my_view) is a ViewMatcher
.perform(click()) // click() is a ViewAction
.check(matches(isDisplayed())); // matches(isDisplayed()) is a ViewAssertion
}
}
Öffnen Sie das DrawerLayout
public final class DrawerLayoutTest {
@Test public void Open_Close_Drawer_Layout() {
onView(withId(R.id.drawer_layout)).perform(actionOpenDrawer());
onView(withId(R.id.drawer_layout)).perform(actionCloseDrawer());
}
public static ViewAction actionOpenDrawer() {
return new ViewAction() {
@Override public Matcher<View> getConstraints() {
return isAssignableFrom(DrawerLayout.class);
}
@Override public String getDescription() {
return "open drawer";
}
@Override public void perform(UiController uiController, View view) {
((DrawerLayout) view).openDrawer(GravityCompat.START);
}
};
}
public static ViewAction actionCloseDrawer() {
return new ViewAction() {
@Override public Matcher<View> getConstraints() {
return isAssignableFrom(DrawerLayout.class);
}
@Override public String getDescription() {
return "close drawer";
}
@Override public void perform(UiController uiController, View view) {
((DrawerLayout) view).closeDrawer(GravityCompat.START);
}
};
}
}
Espresso einfacher UI-Test
UI-Testwerkzeuge
Zwei Hauptwerkzeuge, die heutzutage hauptsächlich für UI-Tests verwendet werden, sind Appium und Espresso.
Appium | Espresso |
---|---|
Blackbox-Test | weiße / graue Boxprüfung |
Was Sie sehen, ist das, was Sie testen können | kann die inneren Abläufe der App ändern und für den Test vorbereiten, z. B. vor dem Ausführen des Tests einige Daten in einer Datenbank oder gemeinsam genutzte Präferenzen speichern |
wird hauptsächlich für Integrations-End-to-End-Tests und gesamte Benutzerflüsse verwendet | Testen der Funktionalität eines Bildschirms und / oder Flusses |
kann abstrahiert werden, so dass der geschriebene Test auf iOS und Android ausgeführt werden kann | Nur Android |
gut unterstützt | gut unterstützt |
unterstützt parallele Tests an mehreren Geräten mit Selengitter | Parallele Tests sind nicht möglich, Plugins wie Spoon sind vorhanden, bis echte Unterstützung von Google herauskommt |
So fügen Sie Espresso zum Projekt hinzu
dependencies {
// Set this dependency so you can use Android JUnit Runner
androidTestCompile 'com.android.support.test:runner:0.5'
// Set this dependency to use JUnit 4 rules
androidTestCompile 'com.android.support.test:rules:0.5'
// Set this dependency to build and run Espresso tests
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
// Set this dependency to build and run UI Automator tests
androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.2.2'
}
HINWEIS Wenn Sie die neuesten Support-Bibliotheken, Anmerkungen usw. verwenden, müssen Sie ältere Versionen von Espresso ausschließen, um Kollisionen zu vermeiden:
// there is a conflict with the test support library (see http://stackoverflow.com/questions/29857695)
// so for now re exclude the support-annotations dependency from here to avoid clashes
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2') {
exclude group: 'com.android.support', module: 'support-annotations'
exclude module: 'support-annotations'
exclude module: 'recyclerview-v7'
exclude module: 'support-v4'
exclude module: 'support-v7'
}
// exclude a couple of more modules here because of <http://stackoverflow.com/questions/29216327> and
// more specifically of <https://code.google.com/p/android-test-kit/issues/detail?id=139>
// otherwise you'll receive weird crashes on devices and dex exceptions on emulators
// Espresso-contrib for DatePicker, RecyclerView, Drawer actions, Accessibility checks, CountingIdlingResource
androidTestCompile('com.android.support.test.espresso:espresso-contrib:2.2.2') {
exclude group: 'com.android.support', module: 'support-annotations'
exclude group: 'com.android.support', module: 'design'
exclude module: 'support-annotations'
exclude module: 'recyclerview-v7'
exclude module: 'support-v4'
exclude module: 'support-v7'
}
//excluded specific packages due to https://code.google.com/p/android/issues/detail?id=183454
androidTestCompile('com.android.support.test.espresso:espresso-intents:2.2.2') {
exclude group: 'com.android.support', module: 'support-annotations'
exclude module: 'support-annotations'
exclude module: 'recyclerview-v7'
exclude module: 'support-v4'
exclude module: 'support-v7'
}
androidTestCompile('com.android.support.test.espresso:espresso-web:2.2.2') {
exclude group: 'com.android.support', module: 'support-annotations'
exclude module: 'support-annotations'
exclude module: 'recyclerview-v7'
exclude module: 'support-v4'
exclude module: 'support-v7'
}
androidTestCompile('com.android.support.test:runner:0.5') {
exclude group: 'com.android.support', module: 'support-annotations'
exclude module: 'support-annotations'
exclude module: 'recyclerview-v7'
exclude module: 'support-v4'
exclude module: 'support-v7'
}
androidTestCompile('com.android.support.test:rules:0.5') {
exclude group: 'com.android.support', module: 'support-annotations'
exclude module: 'support-annotations'
exclude module: 'recyclerview-v7'
exclude module: 'support-v4'
exclude module: 'support-v7'
}
Abgesehen von diesen Importen ist es erforderlich, Android-Testläufer zu build.gradle android.defaultConfig hinzuzufügen:
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
Gerätkonfiguration
Für nicht flockige Tests wird empfohlen, die folgenden Einstellungen an Ihren Geräten vorzunehmen:
- Entwickleroptionen / Animationen deaktivieren - reduziert die Flakynität von Tests
- Entwickleroptionen / Wach bleiben - wenn Sie dedizierte Geräte für Tests haben, ist dies nützlich
- Entwickleroptionen / Logger-Puffergrößen: Legen Sie eine höhere Anzahl fest, wenn Sie sehr große Testreihen auf Ihrem Telefon ausführen
- Accessibility / Touch & Hold-Verzögerung - lang, um Probleme beim Tippen auf Espresso zu vermeiden
Ziemlich ein Setup aus der realen Welt? Nun, wenn das nicht möglich ist, werfen wir einen Blick auf die Einrichtung eines kleinen Tests
Test schreiben
Nehmen wir an, wir haben den folgenden Bildschirm: Der Bildschirm enthält:
- Texteingabefeld - R.id.textEntry
- Schaltfläche, die die Snackbar mit eingetipptem Text anzeigt - R.id.shownSnackbarBtn
- Snackbar, die den vom Benutzer eingegebenen Text enthalten sollte - android.support.design.R.id.snackbar_text
Lassen Sie uns nun eine Klasse erstellen, die unseren Fluss testet:
/**
* Testing of the snackbar activity.
**/
@RunWith(AndroidJUnit4.class)
@LargeTest
public class SnackbarActivityTest{
//espresso rule which tells which activity to start
@Rule
public final ActivityTestRule<SnackbarActivity> mActivityRule =
new ActivityTestRule<>(SnackbarActivity.class, true, false);
@Override
public void tearDown() throws Exception {
super.tearDown();
//just an example how tear down should cleanup after itself
mDatabase.clear();
mSharedPrefs.clear();
}
@Override
public void setUp() throws Exception {
super.setUp();
//setting up your application, for example if you need to have a user in shared
//preferences to stay logged in you can do that for all tests in your setup
User mUser = new User();
mUser.setToken("randomToken");
}
/**
*Test methods should always start with "testXYZ" and it is a good idea to
*name them after the intent what you want to test
**/
@Test
public void testSnackbarIsShown() {
//start our activity
mActivityRule.launchActivity(null);
//check is our text entry displayed and enter some text to it
String textToType="new snackbar text";
onView(withId(R.id.textEntry)).check(matches(isDisplayed()));
onView(withId(R.id.textEntry)).perform(typeText(textToType));
//click the button to show the snackbar
onView(withId(R.id.shownSnackbarBtn)).perform(click());
//assert that a view with snackbar_id with text which we typed and is displayed
onView(allOf(withId(android.support.design.R.id.snackbar_text),
withText(textToType))) .check(matches(isDisplayed()));
}
}
Wie Sie bemerkt haben, gibt es 3-4 Dinge, die Sie häufig bemerken:
onView (withXYZ) <- viewMatchers Mit diesen können Sie Elemente auf dem Bildschirm finden
perform (click ()) <- viewActions, Sie können Aktionen für Elemente ausführen, die Sie zuvor gefunden haben
check (matches (isDisplayed ())) <- viewAssertions: Überprüft, ob Sie die zuvor gefundenen Bildschirme ausführen möchten
Alle diese und viele andere finden Sie hier: https://google.github.io/android-testing-support-library/docs/espresso/cheatsheet/index.html
Jetzt können Sie den Test ausführen, indem Sie mit der rechten Maustaste auf den Klassennamen / Test klicken und Test ausführen oder mit dem Befehl auswählen:
./gradlew connectedFLAVORNAMEAndroidTest
Navigation nach oben
@Test
public void testUpNavigation() {
intending(hasComponent(ParentActivity.class.getName())).respondWith(new Instrumentation.ActivityResult(0, null));
onView(withContentDescription("Navigate up")).perform(click());
intended(hasComponent(ParentActivity.class.getName()));
}
Beachten Sie, dass dies eine Problemumgehung ist und mit anderen Ansichten kollidieren wird, die dieselbe Inhaltsbeschreibung haben.
Ausführen einer Aktion für eine Ansicht
Es ist möglich, ViewActions
für eine Ansicht mithilfe der Perform-Methode auszuführen.
Die ViewActions
Klasse stellt ViewActions
für die häufigsten Aktionen bereit, z.
ViewActions.click()
ViewActions.typeText()
ViewActions.clearText()
Um zum Beispiel auf die Ansicht zu klicken:
onView(...).perform(click());
onView(withId(R.id.button_simple)).perform(click());
Sie können mehrere Aktionen mit einem Perform-Aufruf ausführen:
onView(...).perform(typeText("Hello"), click());
Wenn sich die Ansicht, mit der Sie arbeiten, in einer ScrollView
(vertikal oder horizontal) befindet, sollten Sie vorhergehende Aktionen in Betracht ziehen, die eine Anzeige der Ansicht (wie click()
und typeText()
) mit scrollTo()
. Dadurch wird sichergestellt, dass die Ansicht angezeigt wird, bevor mit der anderen Aktion fortgefahren wird:
onView(...).perform(scrollTo(), click());
Eine Ansicht mit onView finden
Mit den ViewMatchers
können Sie Ansichten in der aktuellen Ansichtshierarchie finden.
Um eine Ansicht zu finden, verwenden Sie die Methode onView()
mit einem View Matcher, der die richtige Ansicht auswählt. Die onView()
-Methoden geben ein Objekt vom Typ ViewInteraction
.
Zum Beispiel ist es so einfach, eine Ansicht anhand ihrer R.id
:
onView(withId(R.id.my_view))
Eine Ansicht mit einem Text suchen:
onView(withText("Hello World"))
Espresso benutzerdefinierte Matchers
Espresso verfügt standardmäßig über viele Übereinstimmungen, mit deren Hilfe Sie Ansichten finden, die Sie für einige Überprüfungen oder Interaktionen mit ihnen benötigen.
Die wichtigsten finden Sie im folgenden Spickzettel:
https://google.github.io/android-testing-support-library/docs/espresso/cheatsheet/
Einige Beispiele für Matcher sind:
- withId (R.id.ID_of_object_you_are_looking_for);
- withText ("Text, den Sie für ein Objekt erwarten");
- isDisplayed () <- Prüfen Sie, ob die Ansicht sichtbar ist
- doesNotExist () <- Überprüfen Sie, ob die Ansicht nicht vorhanden ist
All dies ist sehr nützlich für den täglichen Gebrauch. Wenn Sie jedoch komplexere Ansichten haben, können Ihre benutzerdefinierten Matcher die Tests lesbarer machen und sie an verschiedenen Stellen wiederverwenden.
Es gibt zwei gängige Typen von Matchern, die Sie erweitern können: TypeSafeMatcher BoundedMatcher
Um TypeSafeMatcher zu implementieren, müssen Sie die Instanz überprüfen. Wenn die Ansicht, für die Sie eine Bestätigung ausführen, der richtige Typ ist, in dem Sie einige ihrer Eigenschaften mit einem Wert vergleichen, den Sie einem Matcher bereitgestellt haben.
Geben Sie zum Beispiel sicheres Matcher ein, das eine Bildansicht überprüft.
public class DrawableMatcher extends TypeSafeMatcher<View> {
private @DrawableRes final int expectedId;
String resourceName;
public DrawableMatcher(@DrawableRes int expectedId) {
super(View.class);
this.expectedId = expectedId;
}
@Override
protected boolean matchesSafely(View target) {
//Type check we need to do in TypeSafeMatcher
if (!(target instanceof ImageView)) {
return false;
}
//We fetch the image view from the focused view
ImageView imageView = (ImageView) target;
if (expectedId < 0) {
return imageView.getDrawable() == null;
}
//We get the drawable from the resources that we are going to compare with image view source
Resources resources = target.getContext().getResources();
Drawable expectedDrawable = resources.getDrawable(expectedId);
resourceName = resources.getResourceEntryName(expectedId);
if (expectedDrawable == null) {
return false;
}
//comparing the bitmaps should give results of the matcher if they are equal
Bitmap bitmap = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
Bitmap otherBitmap = ((BitmapDrawable) expectedDrawable).getBitmap();
return bitmap.sameAs(otherBitmap);
}
@Override
public void describeTo(Description description) {
description.appendText("with drawable from resource id: ");
description.appendValue(expectedId);
if (resourceName != null) {
description.appendText("[");
description.appendText(resourceName);
description.appendText("]");
}
}
}
Die Verwendung des Streichholzspielers könnte folgendermaßen dargestellt werden:
public static Matcher<View> withDrawable(final int resourceId) {
return new DrawableMatcher(resourceId);
}
onView(withDrawable(R.drawable.someDrawable)).check(matches(isDisplayed()));
Bounded Matcher sind ähnlich, Sie müssen die Typprüfung jedoch nicht durchführen, da dies jedoch automatisch für Sie erledigt wird:
/**
* Matches a {@link TextInputFormView}'s input hint with the given resource ID
*
* @param stringId
* @return
*/
public static Matcher<View> withTextInputHint(@StringRes final int stringId) {
return new BoundedMatcher<View, TextInputFormView>(TextInputFormView.class) {
private String mResourceName = null;
@Override
public void describeTo(final Description description) {
//fill these out properly so your logging and error reporting is more clear
description.appendText("with TextInputFormView that has hint ");
description.appendValue(stringId);
if (null != mResourceName) {
description.appendText("[");
description.appendText(mResourceName);
description.appendText("]");
}
}
@Override
public boolean matchesSafely(final TextInputFormView view) {
if (null == mResourceName) {
try {
mResourceName = view.getResources().getResourceEntryName(stringId);
} catch (Resources.NotFoundException e) {
throw new IllegalStateException("could not find string with ID " + stringId, e);
}
}
return view.getResources().getString(stringId).equals(view.getHint());
}
};
}
Weitere Informationen zu Matchern finden Sie unter:
https://developer.android.com/reference/android/support/test/espresso/matcher/ViewMatchers.html
Insgesamt Espresso
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
androidTestCompile 'com.android.support.test:runner:0.5'
ViewMatchers - Eine Sammlung von Objekten, die Matcher<? super View>
implementieren Matcher<? super View>
Schnittstelle. Sie können eine oder mehrere davon an die onView
Methode übergeben, um eine Ansicht in der aktuellen Ansichtshierarchie zu onView
.
ViewActions - Eine Auflistung von ViewActions
, die an die ViewInteraction.perform()
Methode übergeben werden können (z. B. click()
).
ViewAssertions - Eine Auflistung von ViewAssertions
, an die die ViewInteraction.check()
-Methode übergeben werden kann. In den meisten Fällen verwenden Sie die Übereinstimmungsassertion, bei der mithilfe eines View-Matchers der Status der aktuell ausgewählten Ansicht bestätigt wird.
Espresso Spickzettel von Google
Geben Sie Text in EditText ein
onView(withId(R.id.edt_name)).perform(typeText("XYZ"));
closeSoftKeyboard();
Ausführen Klicken Sie auf Ansicht
onView(withId(R.id.btn_id)).perform(click());
Überprüfen der Ansicht wird angezeigt
onView(withId(R.id.edt_pan_number)).check(ViewAssertions.matches((isDisplayed())));
Gruppieren Sie eine Sammlung von Testklassen in einer Testsuite
Sie können die Ausführung Ihrer instrumentierten Komponententests zur Definition einer Suite organisieren .
/**
* Runs all unit tests.
*/
@RunWith(Suite.class)
@Suite.SuiteClasses({MyTest1.class ,
MyTest2.class,
MyTest3.class})
public class AndroidTestSuite {}
In AndroidStudio können Sie dann Gradle ausführen oder eine neue Konfiguration festlegen, z.
Testsuiten können verschachtelt sein.