Espresso oferuje mechanizmy, które umożliwiają przewinięcie listy lub wykonanie na niej działania w dwóch typy list: widoki adapterów i użytkowników funkcji recyklingu.
Gdy masz do czynienia z listami, zwłaszcza tymi utworzonymi za pomocą tagów RecyclerView
lub
AdapterView
obiekt, który Cię interesuje, może być nawet
ponieważ widoczna jest tylko niewielka liczba dzieci.
poddawany recyklingowi podczas przewijania. W tym przypadku nie można użyć metody scrollTo()
ponieważ wymaga istniejącego widoku.
Interakcja z elementami listy widoku adaptera
Zamiast używać metody onView()
, rozpocznij wyszukiwanie od metody onData()
i
i dopasowujesz dane powiązane z widokiem, który chcesz dopasować.
Espresso wyszuka wiersz w obiekcie Adapter
i
w widocznym obszarze.
Dopasowanie danych za pomocą dopasowania widoku niestandardowego
Poniższa aktywność zawiera pole ListView
, które jest oparte na: SimpleAdapter
z danymi każdego wiersza w obiekcie Map<String, Object>
.
Każda mapa zawiera 2 wpisy: klucz "STR"
zawierający ciąg, taki jak
"item: x"
oraz klucz "LEN"
zawierający Integer
, który reprezentuje
ich długość. Na przykład:
{"STR" : "item: 0", "LEN": 7}
Kod kliknięcia w wierszu z „item: 50”. wygląda tak:
onData(allOf(`is`(instanceOf(Map::class.java)), hasEntry(equalTo("STR"),
`is`("item: 50")))).perform(click())
onData(allOf(is(instanceOf(Map.class)), hasEntry(equalTo("STR"), is("item: 50"))))
.perform(click());
Espresso przewija listę automatycznie w razie potrzeby.
Przyjrzyjmy się urządzeniu Matcher<Object>
w środku onData()
.
Metoda is(instanceOf(Map.class))
zawęża wyszukiwanie do dowolnego elementu
AdapterView
, która jest wspierana przez obiekt Map
.
W naszym przypadku ten aspekt zapytania pasuje do każdego wiersza widoku listy, ale chcemy kliknąć konkretny element, więc zawężamy wyszukiwanie dalej według tych kryteriów:
Ten obiekt Matcher<String, Object>
będzie pasować do każdej mapy zawierającej wpis z
klucz "STR"
i wartość "item: 50"
. Ponieważ kod służący do wyszukiwania
Chcemy używać go w innych lokalizacjach,
withItemContent
dopasowanie do tego wyniku:
return object : BoundedMatcher<Object, Map>(Map::class.java) {
override fun matchesSafely(map: Map): Boolean {
return hasEntry(equalTo("STR"), itemTextMatcher).matches(map)
}
override fun describeTo(description: Description) {
description.appendText("with item content: ")
itemTextMatcher.describeTo(description)
}
}
return new BoundedMatcher<Object, Map>(Map.class) {
@Override
public boolean matchesSafely(Map map) {
return hasEntry(equalTo("STR"), itemTextMatcher).matches(map);
}
@Override
public void describeTo(Description description) {
description.appendText("with item content: ");
itemTextMatcher.describeTo(description);
}
};
Używa się funkcji BoundedMatcher
jako podstawy, ponieważ dopasowujemy tylko obiekty danego typu.
Map
Zastąp metodę matchesSafely()
, dodając do znalezionego dopasowania
wcześniej i dopasuj ją do parametru Matcher<String>
, który można przekazać jako
. Dzięki temu możesz zadzwonić pod numer withItemContent(equalTo("foo"))
. Aby uzyskać kod
możesz utworzyć kolejne dopasowanie, które wywołuje już operatory equalTo()
i
akceptuje obiekt String
:
fun withItemContent(expectedText: String): Matcher<Object> {
checkNotNull(expectedText)
return withItemContent(equalTo(expectedText))
}
public static Matcher<Object> withItemContent(String expectedText) {
checkNotNull(expectedText);
return withItemContent(equalTo(expectedText));
}
Teraz kod, który trzeba kliknąć, jest prosty:
onData(withItemContent("item: 50")).perform(click())
onData(withItemContent("item: 50")).perform(click());
Pełny kod tego testu znajduje się w metodzie testClickOnItem50()
w ciągu
AdapterViewTest
.
klasa i
ten niestandardowy LongListMatchers
w usłudze GitHub.
Dopasowanie do konkretnego widoku podrzędnego
Powyższy przykład generuje kliknięcie w środku całego wiersza parametru ListView
.
Co jednak, jeśli chcemy przeprowadzić działanie na konkretnym elemencie podrzędnym wiersza? Na przykład możemy
chce kliknąć drugą kolumnę w wierszu LongListActivity
,
, który wyświetla wartość String.length zawartości pierwszej kolumny:
Wystarczy, że dodasz specyfikację onChildView()
do swojej implementacji
DataInteraction
:
onData(withItemContent("item: 60"))
.onChildView(withId(R.id.item_size))
.perform(click())
onData(withItemContent("item: 60"))
.onChildView(withId(R.id.item_size))
.perform(click());
Interakcja z elementami listy widoku recyklingu
Obiekty RecyclerView
działają inaczej niż obiekty AdapterView
, więc
Nie możesz używać usługi onData()
do interakcji z nimi.
Aby korzystać z obiektów RecyclerView za pomocą Espresso, możesz skorzystać z
Pakiet espresso-contrib
, który zawiera kolekcję
RecyclerViewActions
.
które pozwalają przewijać elementy w wybrane miejsca lub wykonywać na nich różne czynności:
scrollTo()
– powoduje przewijanie do pasującego widoku, jeśli istnieje.scrollToHolder()
– przewija stronę do pasującego właściciela widoku, jeśli istnieje.scrollToPosition()
– przewija do określonej pozycji.actionOnHolderItem()
– wykonuje czynność wyświetlenia w przypadku pasującego właściciela widoku.actionOnItem()
– wykonuje działanie związane z wyświetleniem pasującego widoku danych.actionOnItemAtPosition()
– wykonuje działania związane z widokiem danych w określonym miejscu.
Poniższe fragmenty kodu zawierają kilka przykładów RecyclerViewSample przykład:
@Test(expected = PerformException::class)
fun itemWithText_doesNotExist() {
// Attempt to scroll to an item that contains the special text.
onView(ViewMatchers.withId(R.id.recyclerView))
.perform(
// scrollTo will fail the test if no item matches.
RecyclerViewActions.scrollTo(
hasDescendant(withText("not in the list"))
)
)
}
@Test(expected = PerformException.class)
public void itemWithText_doesNotExist() {
// Attempt to scroll to an item that contains the special text.
onView(ViewMatchers.withId(R.id.recyclerView))
// scrollTo will fail the test if no item matches.
.perform(RecyclerViewActions.scrollTo(
hasDescendant(withText("not in the list"))
));
}
@Test fun scrollToItemBelowFold_checkItsText() {
// First, scroll to the position that needs to be matched and click on it.
onView(ViewMatchers.withId(R.id.recyclerView))
.perform(
RecyclerViewActions.actionOnItemAtPosition(
ITEM_BELOW_THE_FOLD,
click()
)
)
// Match the text in an item below the fold and check that it's displayed.
val itemElementText = "${activityRule.activity.resources
.getString(R.string.item_element_text)} ${ITEM_BELOW_THE_FOLD.toString()}"
onView(withText(itemElementText)).check(matches(isDisplayed()))
}
@Test
public void scrollToItemBelowFold_checkItsText() {
// First, scroll to the position that needs to be matched and click on it.
onView(ViewMatchers.withId(R.id.recyclerView))
.perform(RecyclerViewActions.actionOnItemAtPosition(ITEM_BELOW_THE_FOLD,
click()));
// Match the text in an item below the fold and check that it's displayed.
String itemElementText = activityRule.getActivity().getResources()
.getString(R.string.item_element_text)
+ String.valueOf(ITEM_BELOW_THE_FOLD);
onView(withText(itemElementText)).check(matches(isDisplayed()));
}
@Test fun itemInMiddleOfList_hasSpecialText() {
// First, scroll to the view holder using the isInTheMiddle() matcher.
onView(ViewMatchers.withId(R.id.recyclerView))
.perform(RecyclerViewActions.scrollToHolder(isInTheMiddle()))
// Check that the item has the special text.
val middleElementText = activityRule.activity.resources
.getString(R.string.middle)
onView(withText(middleElementText)).check(matches(isDisplayed()))
}
@Test
public void itemInMiddleOfList_hasSpecialText() {
// First, scroll to the view holder using the isInTheMiddle() matcher.
onView(ViewMatchers.withId(R.id.recyclerView))
.perform(RecyclerViewActions.scrollToHolder(isInTheMiddle()));
// Check that the item has the special text.
String middleElementText =
activityRule.getActivity().getResources()
.getString(R.string.middle);
onView(withText(middleElementText)).check(matches(isDisplayed()));
}
Dodatkowe materiały
Więcej informacji o używaniu list Espresso w testach na Androidzie znajdziesz w poniższe zasoby.
Próbki
- DataAdapterSample:
Prezentuje punkt wejścia
onData()
do Espresso, listy iAdapterView
obiektów.