لیست اسپرسو

اسپرسو مکانیسم هایی را برای اسکرول کردن یا عمل کردن روی یک آیتم خاص برای دو نوع لیست ارائه می دهد: نمای آداپتور و نماهای بازیافت.

هنگام سر و کار با لیست‌ها، به‌ویژه آنهایی که با یک شی RecyclerView یا AdapterView ایجاد شده‌اند، نمای مورد علاقه شما ممکن است حتی روی صفحه نباشد زیرا فقط تعداد کمی از کودکان نمایش داده می‌شوند و در حین پیمایش بازیافت می‌شوند. متد scrollTo() را نمی توان در این مورد استفاده کرد زیرا به نمای موجود نیاز دارد.

تعامل با آیتم های لیست نمایش آداپتور

به جای استفاده از متد onView() ، جستجوی خود را با onData() شروع کنید و یک تطبیق در برابر داده‌هایی که از نمای مورد نظر شما پشتیبانی می‌کنند، تهیه کنید. اسپرسو تمام کارهای مربوط به یافتن ردیف را در شیء Adapter و قابل مشاهده کردن آیتم در ویوپورت انجام می دهد.

داده ها را با استفاده از تطبیق نمایش سفارشی مطابقت دهید

فعالیت زیر حاوی ListView است که توسط SimpleAdapter پشتیبانی می‌شود که داده‌ها را برای هر ردیف در یک Map<String, Object> نگهداری می‌کند.

فعالیت لیستی که در حال حاضر روی صفحه نمایش داده می شود حاوی لیستی با 23 مورد است. هر آیتم دارای یک عدد است که به عنوان یک رشته ذخیره می شود و به عدد دیگری نگاشت شده است که به جای آن به عنوان یک شی ذخیره می شود.

هر نقشه دارای دو ورودی است: یک کلید "STR" که حاوی یک رشته است، مانند "item: x" و یک کلید "LEN" که حاوی یک Integer است که طول محتوا را نشان می دهد. به عنوان مثال:

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

کد کلیک بر روی ردیف با "اقلام: 50" به شکل زیر است:

کاتلین

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

توجه داشته باشید که اسپرسو در صورت نیاز به صورت خودکار در لیست پیمایش می کند.

بیایید Matcher<Object> در داخل onData() جدا کنیم. متد is(instanceOf(Map.class)) جستجو را به هر آیتمی از AdapterView محدود می‌کند که توسط یک شی Map پشتیبانی می‌شود.

در مورد ما، این جنبه از پرس و جو با هر ردیف از نمای فهرست مطابقت دارد، اما ما می خواهیم به طور خاص روی یک مورد کلیک کنیم، بنابراین جستجو را با این موارد محدودتر می کنیم:

کاتلین

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

جاوا

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

این Matcher<String, Object> هر نقشه ای را که حاوی ورودی با کلید "STR" و مقدار "item: 50" مطابقت می دهد. از آنجا که کد جستجوی این طولانی است و می‌خواهیم از آن در مکان‌های دیگر استفاده کنیم، بیایید یک تطبیق سفارشی withItemContent برای آن بنویسیم:

کاتلین

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

شما از یک BoundedMatcher به عنوان پایه استفاده می کنید زیرا فقط با اشیاء از نوع Map مطابقت دارد. متد matchesSafely() را نادیده بگیرید، مطابقی را که قبلا پیدا شده بود قرار دهید، و آن را با یک Matcher<String> که می توانید به عنوان آرگومان ارسال کنید مطابقت دهید. این به شما امکان می دهد withItemContent(equalTo("foo")) تماس بگیرید. برای اختصار کد، می‌توانید تطبیق‌دهنده دیگری ایجاد کنید که قبلا equalTo() را فراخوانی کرده و یک شی 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));
}

اکنون کد کلیک بر روی مورد ساده است:

کاتلین

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

جاوا

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

برای کد کامل این تست، نگاهی به متد testClickOnItem50() در کلاس AdapterViewTest و این تطبیق LongListMatchers سفارشی در GitHub بیندازید.

با یک نمای کودک خاص مطابقت دهید

نمونه بالا یک کلیک در وسط کل ردیف یک ListView صادر می کند. اما اگر بخواهیم فرزند خاصی از ردیف را عمل کنیم، چه؟ برای مثال، می‌خواهیم روی ستون دوم سطر LongListActivity کلیک کنیم که طول String.length محتوای ستون اول را نشان می‌دهد:

در این مثال، استخراج تنها طول یک قطعه خاص از محتوا سودمند خواهد بود. این فرآیند شامل تعیین مقدار ستون دوم در یک ردیف است.

فقط یک مشخصه onChildView() را به پیاده سازی 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());

تعامل با موارد لیست نمایش بازیافت

اشیاء RecyclerView متفاوت از اشیاء AdapterView کار می کنند، بنابراین onData() نمی تواند برای تعامل با آنها استفاده شود.

برای تعامل با RecyclerViews با استفاده از Espresso، می‌توانید از بسته espresso-contrib استفاده کنید، که مجموعه‌ای از RecyclerViewActions دارد که می‌تواند برای پیمایش به موقعیت‌ها یا انجام اقدامات روی موارد استفاده شود:

  • scrollTo() - در صورت وجود، به نمای منطبق می رود.
  • scrollToHolder() - در صورت وجود، به نگهدارنده نمایش منطبق می رود.
  • scrollToPosition() - به یک موقعیت خاص پیمایش می کند.
  • actionOnHolderItem() - یک عملکرد View را بر روی یک نگهدارنده نمایش منطبق انجام می دهد.
  • actionOnItem() - یک عملکرد View را روی یک نمای همسان انجام می دهد.
  • actionOnItemAtPosition() - یک ViewAction را روی یک view در یک موقعیت خاص انجام می دهد.

قطعه‌های زیر نمونه‌هایی از نمونه RecyclerViewSample را نشان می‌دهند:

کاتلین

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

منابع اضافی

برای اطلاعات بیشتر در مورد استفاده از لیست اسپرسو در تست های اندروید، به منابع زیر مراجعه کنید.

نمونه ها

  • DataAdapterSample : نقطه ورودی onData() برای اسپرسو، برای لیست ها و اشیاء AdapterView نمایش می دهد.