Android
Testar UI med Espresso
Sök…
Anmärkningar
Espresso
Espresso cheat sheet hjälper dig att skriva dina tester och vad du vill testa:
https://google.github.io/android-testing-support-library/docs/espresso/cheatsheet/
Den officiella dokumentationen är alltid en bra plats för referens:
https://google.github.io/android-testing-support-library/docs/espresso/index.html
Avancerade förslag på espressomaterial från Google: https://www.youtube.com/watch?v=isihPOY2vS4
Felsökning
- När du försöker bläddra måste du först stänga tangentbordet:
Se upp: att inte använda "Espresso" -versionen kommer inte att göra någonting när den används utanför en ViewAction. Det kanske inte är uppenbart om du har en import till ViewAction-versionen eftersom de har exakt samma metodnamn.
ViewActions.closeSoftKeyboard;
Espresso.closeSoftKeyboard();
- När du kör tester tillsammans i en svit snarare än individuellt, ska du vara medveten om att aktiviteten från det föregående testet fortfarande kan köras. Lita inte på att det tidigare testets onDestroy () kallas innan de aktuella testerna onResume (). Det visar sig att detta faktiskt är ett fel : http://b.android.com/201513
Ställ in Espresso
build.gradle
till nästa beroenden i build.gradle
filen i din Android-appmodul:
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'
}
Ange AndroidJUnitRunner
för testInstrumentationRunner
parametern i build.gradle
filen.
android {
defaultConfig {
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
}
Lägg till detta beroende för att tillhandahålla avsiktligt håna stöd
androidTestCompile 'com.android.support.test.espresso:espresso-intents:2.2.2'
Och lägg till den här för support för webbvisningstest
// Espresso-web for WebView support
androidTestCompile 'com.android.support.test.espresso:espresso-web:2.2.2'
Skapa Espresso Test Class
Placera nästa java-klass i src / androidTest / java och kör den.
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
}
}
Öppna Stäng 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);
}
};
}
}
Espressos enkla UI-test
UI-testverktyg
Två huvudverktyg som idag mest används för UI-test är Appium och Espresso.
Appium | Espresso |
---|---|
blackbox-test | vit / grå box testning |
vad du ser är vad du kan testa | kan ändra appens inre funktioner och förbereda den för testning, t.ex. spara data i databasen eller delade föredrag innan testet körs |
används mest för att integrera tester från slutet till slut och hela användarflöden | testa funktionaliteten på en skärm och / eller flöde |
kan abstraheras så testskrivna kan köras på iOS och Android | Endast Android |
väl stött | väl stött |
stöder parallella tester på flera enheter med selenät | Parallelltestning är inte uteslutet, plugins som Spoon finns förrän sant Google-support kommer ut |
Hur man lägger till espresso i projektet
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'
}
OBS ! Om du använder senaste supportbibliotek, anteckningar etc. måste du utesluta de äldre versionerna från espresso för att undvika kollisioner:
// 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'
}
Annat än denna import är det nödvändigt att lägga till Android instrumentation testrunner för att build.gradle android.defaultConfig:
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
Enhetsinställning
För icke-flagnigt test rekommenderas att du ställer in följande inställningar på dina enheter:
- Utvecklaralternativ / Inaktivera animationer - minskar testens fläcklighet
- Utvecklaralternativ / Håll dig vaken - om du har dedikerade enheter för test är detta användbart
- Utvecklaralternativ / Loggerbuffertstorlekar - ställ in på högre antal om du kör mycket stora testsviter på din telefon
- Tillgänglighet / Touch & Hold-fördröjning - länge för att undvika problem med att trycka på espresso
Ganska inställning från den verkliga världen ha? Tja nu när det är ur vägen låter oss ta en titt på hur man ställer in ett litet test
Skriva testet
Låt oss anta att vi har följande skärm: Skärmen innehåller:
- textinmatningsfält - R.id.textEntry
- -knappen som visar snackbar med typad text när du klickar på - R.id.shownSnackbarBtn
- snackbar som ska innehålla användartypad text - android.support.design.R.id.snackbar_text
Låt oss nu skapa en klass som testar vårt flöde:
/**
* 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()));
}
}
När du märkte att det finns 3-4 saker du kanske märker kommer ofta:
onView (withXYZ) <- viewMatchers med dem kan du hitta element på skärmen
utför (klicka ()) <- viewActions, du kan utföra åtgärder på element du tidigare hittat
check (matchningar (isDisplayed ())) <- viewAssertions, checkar du vill göra på skärmar som du hittade tidigare
Alla dessa och många andra kan hittas här: https://google.github.io/android-testing-support-library/docs/espresso/cheatsheet/index.html
Det är det, nu kan du köra testet antingen med högerklicka på klassens namn / test och välja Kör test eller med kommando:
./gradlew connectedFLAVORNAMEAndroidTest
Upp navigering
@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()));
}
Observera att detta är en lösning och kommer att kollidera med andra vyer som har samma innehållsbeskrivning.
Utför en åtgärd på en vy
Det är möjligt att utföra ViewActions
på en vy med perform-metoden.
ViewActions
tillhandahåller hjälpmetoder för de vanligaste åtgärderna, som:
ViewActions.click()
ViewActions.typeText()
ViewActions.clearText()
För att till exempel klicka på vyn:
onView(...).perform(click());
onView(withId(R.id.button_simple)).perform(click());
Du kan utföra mer än en åtgärd med ett samtal:
onView(...).perform(typeText("Hello"), click());
Om vyn du arbetar med är belägen i en ScrollView
(vertikal eller horisontell), överväg föregående åtgärder som kräver att vyn ska visas (som click()
och typeText()
) med scrollTo()
. Detta säkerställer att vyn visas innan du fortsätter till den andra åtgärden:
onView(...).perform(scrollTo(), click());
Hitta en vy med onView
Med ViewMatchers
du hitta vy i den aktuella visningshierarkin.
För att hitta en vy använder onView()
metoden onView()
med en visningsmatchare som väljer rätt vy. onView()
returnerar ett objekt av typen ViewInteraction
.
Till exempel är att hitta en vy från dess R.id
lika enkelt som:
onView(withId(R.id.my_view))
Hitta en vy med en text:
onView(withText("Hello World"))
Espresso anpassade matchare
Espresso har som standard många matchare som hjälper dig att hitta vyer som du behöver göra några kontroller eller interaktioner med dem.
De viktigaste finns i följande fuskark:
https://google.github.io/android-testing-support-library/docs/espresso/cheatsheet/
Några exempel på matchare är:
- withId (R.id.ID_of_object_you_are_looking_for);
- withText ("Viss text du förväntar dig att objekt ska ha");
- isDisplayed () <- kontroll är vyn synlig
- doesNotExist () <- kontrollera att vyn inte finns
Alla dessa är mycket användbara för vardagligt bruk, men om du har mer komplexa vyer kan dina anpassade matchare göra testerna mer läsbara och du kan återanvända dem på olika platser.
Det finns två vanligaste matchartyper som du kan utöka: TypeSafeMatcher BoundedMatcher
Implementering av TypeSafeMatcher kräver att du kontrollerar förekomsten av vyn du hävdar mot, om det är rätt typ matchar du några av dess egenskaper mot ett värde som du angav till en matcher.
Skriv till exempel säker matcher som validerar en bildvy med korrekt dragbarhet:
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("]");
}
}
}
Användningen av matchen kan lindas så här:
public static Matcher<View> withDrawable(final int resourceId) {
return new DrawableMatcher(resourceId);
}
onView(withDrawable(R.drawable.someDrawable)).check(matches(isDisplayed()));
Begränsade matchare är liknande du behöver inte göra typkontrollen, eftersom det görs automatiskt för dig:
/**
* 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());
}
};
}
Mer om matcher kan läsas upp på:
https://developer.android.com/reference/android/support/test/espresso/matcher/ViewMatchers.html
Övergripande Espresso
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
androidTestCompile 'com.android.support.test:runner:0.5'
ViewMatchers - En samling av objekt som implementerar Matcher<? super View>
gränssnitt. Du kan skicka en eller flera av dessa till onView
metoden för att hitta en vy i den aktuella visningshierarkin.
ViewActions - En samling ViewActions
som kan skickas till ViewInteraction.perform()
(till exempel click()
).
ViewAssertions - En samling ViewAssertions
som kan skickas ViewInteraction.check()
metoden ViewInteraction.check()
. För det mesta använder du matchningspåståendet, som använder en View-matcher för att hävda tillståndet för den valda vyn.
Espresso fuska blad från Google
Ange text i EditText
onView(withId(R.id.edt_name)).perform(typeText("XYZ"));
closeSoftKeyboard();
Utför Klicka på Visa
onView(withId(R.id.btn_id)).perform(click());
Kontrollera vy visas
onView(withId(R.id.edt_pan_number)).check(ViewAssertions.matches((isDisplayed())));
Gruppera en samling testklasser i en testsvit
Du kan organisera utförandet av dina instrumenterade enhetstester som definierar en svit .
/**
* Runs all unit tests.
*/
@RunWith(Suite.class)
@Suite.SuiteClasses({MyTest1.class ,
MyTest2.class,
MyTest3.class})
public class AndroidTestSuite {}
Sedan i AndroidStudio kan du köra med gradle eller ställa in en ny konfiguration som:
Testsviter kan häckas.