Espresso listeleri

Espresso, iki tür liste için belirli bir öğeye gitme veya öğe üzerinde işlem yapma mekanizmaları sunar: adaptör görünümleri ve geri dönüşümcü görünümleri.

Listelerle çalışırken, özellikle bir RecyclerView veya AdapterView nesnesiyle oluşturulan listelerde, ilgilendiğiniz görünüm ekranda bile görünmeyebilir. Bunun nedeni, çok az sayıda alt öğenin görüntülenmesi ve siz sayfayı kaydırdıkça geri dönüştürülmesidir. Mevcut bir görünüm gerektirdiği için scrollTo() yöntemi bu durumda kullanılamaz.

Bağdaştırıcı görünümü liste öğeleriyle etkileşim kurma

onView() yöntemini kullanmak yerine, aramanızı onData() ile başlatın ve eşleştirmek istediğiniz görünümü destekleyen veriler için bir eşleştirici sağlayın. Espresso, Adapter nesnesindeki satırı bulma ve öğeyi görüntü alanında görünür hale getirme işlemlerinin tamamını yapar.

Verileri özel bir görünüm eşleştirici kullanarak eşleştirme

Aşağıdaki etkinlik, Map<String, Object> nesnesindeki her satıra ait verileri barındıran bir SimpleAdapter tarafından desteklenen bir ListView içerir.

O anda ekranda gösterilen liste etkinliği, 23 öğeli bir liste içerir. Her öğenin farklı bir sayıyla eşlenmiş ve Dize olarak depolanan bir sayısı vardır. Bu sayı, Nesne olarak depolanır.

Her eşlemede iki giriş vardır: "item: x" gibi Dize içeren bir anahtar "STR" ve içeriğin uzunluğunu temsil eden Integer içeren bir "LEN" anahtarı. Örneğin:

{"STR" : "item: 0", "LEN": 7}

"item: 50" içeren satırdaki bir tıklamaya ilişkin kod aşağıdaki gibi görünür:

Kotlin

onData(allOf(`is`(instanceOf(Map::class.java)), hasEntry(equalTo("STR"),
        `is`("item: 50")))).perform(click())

Java

onData(allOf(is(instanceOf(Map.class)), hasEntry(equalTo("STR"), is("item: 50"))))
    .perform(click());

Espresso'nun gerektiğinde listede otomatik olarak kaydırdığını unutmayın.

onData() içindeki Matcher<Object> parçalarını inceleyelim. is(instanceOf(Map.class)) yöntemi, aramayı bir Map nesnesi tarafından desteklenen AdapterView içindeki herhangi bir öğeyle daraltır.

Örneğimizde, sorgunun bu özelliği liste görünümünün her satırıyla eşleşir, ancak özellikle bir öğeyi tıklamak istediğimizden aramayı aşağıdaki şekilde daraltırız:

Kotlin

hasEntry(equalTo("STR"), `is`("item: 50"))

Java

hasEntry(equalTo("STR"), is("item: 50"))

Bu Matcher<String, Object>, "STR" anahtarı ve "item: 50" değerine sahip bir giriş içeren tüm eşlemelerle eşleşir. Bunu aramak için kullanılacak kod uzun olduğundan ve bunu başka konumlarda tekrar kullanmak istediğimizden, bunun için özel bir withItemContent eşleştirici yazalım:

Kotlin

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)
    }
}

Java

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);
    }
};

Yalnızca Map türündeki nesneleri eşleştireceğiniz için temel olarak BoundedMatcher kullanırsınız. Eşleştiriciyi daha önce ekleyerek matchesSafely() yöntemini geçersiz kılın ve bağımsız değişken olarak iletebileceğiniz bir Matcher<String> ile eşleştirin. Bu, withItemContent(equalTo("foo")) numarasını aramanıza olanak tanır. Kodun kısaltması için equalTo() öğesini zaten çağıran ve String nesnesini kabul eden başka bir eşleştirici oluşturabilirsiniz:

Kotlin

fun withItemContent(expectedText: String): Matcher<Object> {
    checkNotNull(expectedText)
    return withItemContent(equalTo(expectedText))
}

Java

public static Matcher<Object> withItemContent(String expectedText) {
    checkNotNull(expectedText);
    return withItemContent(equalTo(expectedText));
}

Artık öğeyi tıklama kodu basittir:

Kotlin

onData(withItemContent("item: 50")).perform(click())

Java

onData(withItemContent("item: 50")).perform(click());

Bu testin tam kodu için AdapterViewTest sınıfındaki testClickOnItem50() yöntemine ve GitHub'daki bu özel LongListMatchers eşleyiciye göz atın.

Belirli bir alt görünümle eşleştirme

Yukarıdaki örnek, tüm ListView satırının ortasına bir tıklama gönderir. Ancak, satırın belirli bir alt öğesi üzerinde çalışmak istersek ne olur? Örneğin, ilk sütundaki içeriğin String.length değerini görüntüleyen LongListActivity satırının ikinci sütununu tıklamak isteriz:

Bu örnekte, yalnızca belirli bir içeriğin uzunluğunu ayıklamak faydalı olur. Bu işlem, bir satırdaki ikinci sütunun değerinin belirlenmesini içerir.

DataInteraction uygulamanıza bir onChildView() spesifikasyonu eklemeniz yeterlidir:

Kotlin

onData(withItemContent("item: 60"))
    .onChildView(withId(R.id.item_size))
    .perform(click())

Java

onData(withItemContent("item: 60"))
    .onChildView(withId(R.id.item_size))
    .perform(click());

Geri dönüşüm görünümü listesindeki öğelerle etkileşime geçme

RecyclerView nesneleri AdapterView nesnelerinden farklı çalışır, bu nedenle onData() bunlarla etkileşim kurmak için kullanılamaz.

Espresso kullanarak RecyclerViews ile etkileşimde bulunmak için espresso-contrib paketini kullanabilirsiniz. Bu pakette, konumlara ilerlemek veya öğeler üzerinde işlem gerçekleştirmek için kullanılabilen bir RecyclerViewActions koleksiyonu bulunur:

  • scrollTo() - Varsa eşleşen görünüme gider.
  • scrollToHolder(): Varsa eşleşen Görüntüleme Sahibi'ne gider.
  • scrollToPosition(): Belirli bir konuma gider.
  • actionOnHolderItem() - Eşleşen bir Görüntüleme Sahibi üzerinde Görüntüleme İşlemi gerçekleştirir.
  • actionOnItem() - Eşleşen bir Görünümde Görüntüleme İşlemi gerçekleştirir.
  • actionOnItemAtPosition() - Belirli bir konumdaki bir görünümde bir ViewAction gerçekleştirir.

Aşağıdaki snippet'lerde RecyclerViewSample örneğinden bazı örnekler yer almaktadır:

Kotlin

@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"))
            )
        )
}

Java

@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"))
            ));
}

Kotlin

@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()))
}

Java

@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()));
}

Kotlin

@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()))
}

Java

@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()));
}

Ek kaynaklar

Android testlerinde Espresso listelerini kullanma hakkında daha fazla bilgi için aşağıdaki kaynaklara bakın.

Sana Özel

  • DataAdapterSample: Listeler ve AdapterView nesneleri için Espresso'nun onData() giriş noktasını gösterir.