توفر Espresso آليات للتمرير إلى عنصر معين أو اتخاذ إجراء بشأنه لنوعين من القوائم: طريقة عرض المحول وعروض إعادة التدوير.
عند التعامل مع القوائم، لا سيّما تلك التي تم إنشاؤها باستخدام عنصر RecyclerView
أو AdapterView
، قد لا تظهر طريقة العرض التي تهمّك حتى على الشاشة، لأنّه يتم عرض عدد قليل فقط من الأطفال وإعادة تدويرها أثناء التنقّل. لا يمكن استخدام طريقة scrollTo()
في هذه الحالة
لأنها تتطلّب طريقة عرض حالية.
التفاعل مع عناصر قائمة عرض المحوّل
بدلاً من استخدام طريقة onView()
، ابدأ البحث باستخدام onData()
، وقدِّم مطابقة مع البيانات التي تدعم الملف الشخصي الذي تريد مطابقته.
ينفّذ قهوة Espresso كل مهام العثور على الصف في العنصر Adapter
ويجعل العنصر مرئيًا في إطار العرض.
مطابقة البيانات باستخدام أداة مطابقة الملفات الشخصية المخصّصة
يحتوي النشاط أدناه على ListView
، ويتم دعمه بواسطة SimpleAdapter
التي تحتوي على بيانات لكل صف في كائن Map<String, Object>
.
تحتوي كل خريطة على إدخالَين: المفتاح "STR"
الذي يتضمّن سلسلة، مثل "item: x"
، والمفتاح "LEN"
الذي يتضمّن Integer
الذي يمثّل طول المحتوى. مثلاً:
{"STR" : "item: 0", "LEN": 7}
يظهر رمز النقرة على الصف الذي يحتوي على "العنصر: 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());
تجدر الإشارة إلى أنّ قهوة الإسبريسو تتنقّل في القائمة تلقائيًا حسب الحاجة.
لنفكك 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
.
لكن ماذا لو أردنا العمل على عنصر ثانوي معين من الصف؟ على سبيل المثال، نود النقر على العمود الثاني من الصف 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()
- لتنفيذ "إجراء عرض" على ملف شخصي في موضع معيّن.
تعرض المقتطفات التالية بعض الأمثلة من المثال 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، يُرجى الرجوع إلى الموارد التالية.
عيّنات
- DataAdapterعيّن:
يعرض نقطة دخول
onData()
لـ Espresso للقوائم والكائناتAdapterView
.