รายการเอสเพรสโซ

Espresso มีกลไกสำหรับเลื่อนหรือดำเนินการกับรายการใดรายการหนึ่ง ซึ่งได้แก่ มุมมองอะแดปเตอร์และมุมมองนักรีไซเคิล

เมื่อจัดการกับรายการ โดยเฉพาะรายการที่สร้างด้วย RecyclerView หรือ AdapterView มุมมองที่คุณสนใจอาจไม่ได้เปิด บนหน้าจอเพราะมีเด็กเพียงไม่กี่คนเท่านั้นที่แสดงและ เป็นการรีไซเคิลขณะที่คุณเลื่อน ใช้เมธอด scrollTo() ไม่ได้ในกรณีนี้ เนื่องจากจำเป็นต้องมีข้อมูลพร็อพเพอร์ตี้อยู่แล้ว

โต้ตอบกับรายการมุมมองรายการของอะแดปเตอร์

แทนที่จะใช้เมธอด onView() ให้เริ่มการค้นหาด้วย onData() และ ระบุตัวจับคู่กับข้อมูลที่สนับสนุนข้อมูลพร็อพเพอร์ตี้ที่คุณต้องการจับคู่ เอสเพรสโซจะทำงานทั้งหมดเพื่อค้นหาแถวในออบเจ็กต์ Adapter และ ซึ่งทำให้รายการปรากฏในวิวพอร์ต

จับคู่ข้อมูลโดยใช้ตัวจับคู่มุมมองที่กำหนดเอง

กิจกรรมด้านล่างมี ListView ซึ่งได้รับการสนับสนุนโดย SimpleAdapter ที่เก็บข้อมูลสำหรับแต่ละแถวในออบเจ็กต์ Map<String, Object>

กิจกรรมรายการที่แสดงบนหน้าจอในปัจจุบันประกอบด้วยรายการที่มี
          23 รายการ แต่ละรายการจะมีตัวเลขที่จัดเก็บเป็นสตริง ซึ่งจับคู่กับ
          หมายเลขอื่น ซึ่งจัดเก็บเป็นออบเจ็กต์แทน

แต่ละแผนที่มี 2 รายการ ได้แก่ คีย์ "STR" ที่มีสตริง เช่น "item: x" และคีย์ "LEN" ที่มี Integer ซึ่งแสดงถึง ความยาวของเนื้อหา เช่น

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

โค้ดสําหรับการคลิกแถวที่มี "item: 50" ซึ่งมีลักษณะดังนี้

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 เลื่อนดูรายการโดยอัตโนมัติตามที่ต้องการ

มาแยก Matcher<Object> ภายใน onData() กัน is(instanceOf(Map.class)) จะจำกัดการค้นหาให้แสดงเฉพาะรายการ AdapterView ซึ่งได้รับการสนับสนุนโดยออบเจ็กต์ Map

ในกรณีของเรา ข้อความค้นหาด้านนี้ตรงกับแถวของมุมมองรายการทุกแถว แต่เรา คลิกรายการหนึ่งๆ โดยเฉพาะ เราจึงจำกัดการค้นหาให้แคบลงด้วยข้อมูลต่อไปนี้

Kotlin

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

Java

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

Matcher<String, Object> นี้จะตรงกับแผนที่ใดๆ ที่มีรายการที่มี คีย์ "STR" และค่า "item: 50" เพราะโค้ดที่ใช้ค้นหา และเราต้องการนำมาใช้ใหม่ในสถานที่อื่นๆ ลองเขียนบรรทัดที่กำหนดเอง ตัวจับคู่ withItemContent สำหรับกรณีนี้:

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

คุณใช้ BoundedMatcher เป็นฐานเนื่องจากเพื่อจับคู่เฉพาะวัตถุประเภท Map ลบล้างเมธอด matchesSafely() โดยใส่ตัวจับคู่ที่พบ ก่อนหน้านี้ และจับคู่กับ Matcher<String> ที่คุณสามารถส่งผ่าน อาร์กิวเมนต์ การดำเนินการนี้จะช่วยให้คุณโทรหา withItemContent(equalTo("foo")) ได้ สำหรับรหัส มีความกระชับ คุณสามารถสร้างตัวจับคู่อื่นที่เรียก equalTo() และ ยอมรับออบเจ็กต์ String:

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

ตอนนี้โค้ดสำหรับคลิกรายการนั้นสามารถทำได้ง่าย:

Kotlin

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

Java

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

สำหรับโค้ดทั้งหมดของการทดสอบนี้ โปรดดูที่เมธอด testClickOnItem50() ภายใน AdapterViewTest ชั้นเรียนและ LongListMatchers ที่กำหนดเองนี้ ตัวจับคู่ใน GitHub

ตรงกับมุมมองย่อยที่เจาะจง

ตัวอย่างด้านบนจะส่งออกการคลิกตรงกลางแถวทั้งแถวของ ListView แต่หากต้องการดำเนินการกับระดับย่อยที่เจาะจง ตัวอย่างเช่น เรา ต้องการคลิกคอลัมน์ที่ 2 ของแถว LongListActivity ซึ่งจะแสดง String.length ของเนื้อหาในคอลัมน์แรก:

ในตัวอย่างนี้ จะเป็นประโยชน์หากจะแยกเฉพาะความยาวของ
          เนื้อหาที่เฉพาะเจาะจง กระบวนการนี้เกี่ยวข้องกับการพิจารณา
          ของคอลัมน์ที่สองในแถว

เพียงเพิ่มข้อกำหนด onChildView() ลงในการใช้งาน DataInteraction:

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

โต้ตอบกับรายการในมุมมองของผู้รีไซเคิล

ออบเจ็กต์ RecyclerView รายการทำงานแตกต่างจากออบเจ็กต์ AdapterView รายการ ดังนั้น ไม่สามารถใช้ onData() เพื่อโต้ตอบกับรายชื่อติดต่อได้

ในการโต้ตอบกับ RecyclerViews โดยใช้ Espresso คุณสามารถใช้ แพ็กเกจ espresso-contrib ซึ่งมีคอลเล็กชัน RecyclerViewActions ที่ใช้เลื่อนไปยังตำแหน่งหรือดำเนินการกับรายการต่างๆ ได้

  • scrollTo() - เลื่อนไปที่มุมมองที่ตรงกัน หากมี
  • scrollToHolder() - เลื่อนไปที่ตัวยึดตำแหน่งการดูที่ตรงกัน (หากมี)
  • scrollToPosition() - เลื่อนไปยังตำแหน่งที่ต้องการ
  • actionOnHolderItem() - ดำเนินการดูกับตัวยึดตำแหน่งการดูที่ตรงกัน
  • actionOnItem() - ดำเนินการดูในข้อมูลพร็อพเพอร์ตี้ที่ตรงกัน
  • actionOnItemAtPosition() - ทำ ViewAction บนข้อมูลพร็อพเพอร์ตี้ที่ตำแหน่งที่เฉพาะเจาะจง

ข้อมูลต่อไปนี้จะแสดงตัวอย่างบางส่วนจาก RecyclerViewSample ตัวอย่าง:

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

แหล่งข้อมูลเพิ่มเติม

หากต้องการข้อมูลเพิ่มเติมเกี่ยวกับการใช้รายการ Espresso ในการทดสอบ Android โปรดดู แหล่งข้อมูลต่อไปนี้

ตัวอย่าง

  • DataAdapterSample: แสดงจุดแรกเข้า onData() ของ Espresso สำหรับรายการและ AdapterView ออบเจ็กต์