يوضح هذا المستند كيفية إكمال مهام الاختبار التلقائي الشائعة باستخدام واجهة برمجة تطبيقات Espresso.
تشجّع واجهة برمجة تطبيقات Espresso مؤلفي الاختبارات على التفكير في ما قد يجربه المستخدم
القيام به أثناء التفاعل مع التطبيق - تحديد مكان عناصر واجهة المستخدم والتفاعل
معهم. وفي الوقت نفسه، يمنع إطار العمل الوصول المباشر إلى الأنشطة.
وطريقة عرض التطبيق لأن الاحتفاظ بهذه الكائنات وتشغيل
عليها خارج مؤشر ترابط واجهة المستخدم مصدرًا رئيسيًا لعملية الاختبار. وبالتالي، سوف
لا يمكن الاطّلاع على طرق مثل getView()
وgetCurrentActivity()
في Espresso API.
لا يزال بإمكانك الاستفادة بأمان من المشاهدات من خلال تطبيق فئاتك الفرعية
"ViewAction
" وViewAssertion
"
مكونات واجهة برمجة التطبيقات
تشمل المكونات الرئيسية لقهوة الإسبريسو ما يلي:
- Espresso: نقطة الدخول إلى التفاعلات مع المشاهدات (عبر
onView()
onData()
). تعرض أيضًا واجهات برمجة التطبيقات التي لا ترتبط بالضرورة بأي ملف شخصي، مثل باسمpressBack()
. - ViewMatchers – مجموعة من الكائنات التي تنفّذ
الواجهة "
Matcher<? super View>
". يمكنك تمرير واحد أو أكثر من هذه إلىonView()
لتحديد موقع عرض ضمن التدرج الهرمي لطريقة العرض الحالية. - ViewActions – مجموعة من
ViewAction
من العناصر التي يمكن تمريرها إلى طريقةViewInteraction.perform()
، مثلclick()
. - ViewAssertions – مجموعة من عناصر
ViewAssertion
التي يمكن اجتاز الطريقةViewInteraction.check()
. في معظم الأوقات، ستستخدم تطابق التأكيد، الذي يستخدم مطابقة المشاهدة لتأكيد حالة العرض المحدد حاليًا.
مثال:
Kotlin
// withId(R.id.my_view) is a ViewMatcher // click() is a ViewAction // matches(isDisplayed()) is a ViewAssertion onView(withId(R.id.my_view)) .perform(click()) .check(matches(isDisplayed()))
Java
// withId(R.id.my_view) is a ViewMatcher // click() is a ViewAction // matches(isDisplayed()) is a ViewAssertion onView(withId(R.id.my_view)) .perform(click()) .check(matches(isDisplayed()));
العثور على طريقة عرض
في الغالبية العظمى من الحالات، تستخدم طريقة onView()
مطابقة الهمكريس
من المتوقّع أن يتطابق مع طريقة عرض واحدة - واحدة فقط - ضمن طريقة العرض الحالية
التسلسل الهرمي. إنّ المطابقات فعّالة وستكون مألوفة بالنسبة إلى الذين استخدموا التطبيق.
باستخدام Mockito أو JUnit. إذا لم تكن على دراية بمطابقة
الهامكريست، يمكننا
أن تبدأ بإلقاء نظرة سريعة على هذه
عرضنا التقديمي.
غالبًا ما يتضمن العرض المطلوب R.id
فريدًا ومطابقة withId
بسيطة
لتضييق نطاق البحث عن طرق العرض. ومع ذلك، هناك العديد من الحالات المشروعة التي
لا يمكن تحديد R.id
في وقت تطوير الاختبار. على سبيل المثال، يتضمن الملف الشخصي المحدد
قد لا يحتوي على R.id
أو R.id
غير فريد. هذا يمكن أن يجعل
إن إجراء اختبارات قياس حالة التطبيق
هش ومعقدة للكتابة لأن الطريقة العادية
الوصول إلى العرض — باستخدام findViewById()
— لا يعمل. وبالتالي، يمكنك
الحاجة إلى الوصول إلى الأعضاء الخاصين في النشاط أو الجزء الذين يملكون العرض أو
للعثور على حاوية تتضمّن R.id
معروفًا والانتقال إلى محتواها للاطّلاع على
معين.
تتعامل قهوة الإسبريسو مع هذه المشكلة بوضوح من خلال السماح لك بتضييق نطاق العرض
باستخدام عناصر ViewMatcher
الحالية أو عناصرك المخصّصة
يمكنك العثور على طريقة عرض من خلال R.id
بسهولة من خلال الاتصال بـ onView()
:
Kotlin
onView(withId(R.id.my_view))
Java
onView(withId(R.id.my_view));
في بعض الأحيان، تتم مشاركة قيم R.id
بين طرق عرض متعددة. وعندما يحدث هذا
محاولة استخدام قيمة R.id
معيّنة، تمنحك استثناءً، مثل
AmbiguousViewMatcherException
توفِّر لك رسالة الاستثناء نصًا
تمثيلاً للتسلسل الهرمي الحالي لطريقة العرض، والذي يمكنك البحث عنه والعثور عليه
مرّات المشاهدة التي تتطابق مع R.id
غير الفريدة:
java.lang.RuntimeException: androidx.test.espresso.AmbiguousViewMatcherException This matcher matches multiple views in the hierarchy: (withId: is <123456789>) ... +----->SomeView{id=123456789, res-name=plus_one_standard_ann_button, visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true, window-focus=true, is-focused=false, is-focusable=false, enabled=true, selected=false, is-layout-requested=false, text=, root-is-layout-requested=false, x=0.0, y=625.0, child-count=1} ****MATCHES**** | +------>OtherView{id=123456789, res-name=plus_one_standard_ann_button, visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true, window-focus=true, is-focused=false, is-focusable=true, enabled=true, selected=false, is-layout-requested=false, text=Hello!, root-is-layout-requested=false, x=0.0, y=0.0, child-count=1} ****MATCHES****
من خلال استعراض السمات المختلفة للمشاهدات، قد تجد
الخصائص التي يمكن التعرف عليها. في المثال أعلاه، تحتوي إحدى طرق العرض على النص
"Hello!"
يمكنك استخدام هذا لتضييق نطاق البحث باستخدام تركيبة
المطابقات:
Kotlin
onView(allOf(withId(R.id.my_view), withText("Hello!")))
Java
onView(allOf(withId(R.id.my_view), withText("Hello!")));
يمكنك أيضًا اختيار عدم عكس أي من المطابقات:
Kotlin
onView(allOf(withId(R.id.my_view), not(withText("Unwanted"))))
Java
onView(allOf(withId(R.id.my_view), not(withText("Unwanted"))));
الاطّلاع على ViewMatchers
لمطابقي طرق العرض المقدّمة من Espresso.
الاعتبارات
- في تطبيق جيد التصرف، جميع طرق العرض التي يمكن للمستخدم التفاعل معها
أن تتضمّن نصًا وصفيًا أو وصفًا للمحتوى. عرض
تسهيل الوصول إلى التطبيقات للحصول على المزيد
التفاصيل. إذا لم تتمكن من تضييق نطاق بحث باستخدام
withText()
أوwithContentDescription()
، ننصحك باعتباره خطأ في إمكانية الوصول. - استخدام أقل مُطابق وصفي يعثر على طريقة العرض الواحدة التي تبحث عنها
من أجله. لا تفرط في التحديد لأنّ ذلك سيجبر إطار العمل على القيام بعمل أكثر من
ضروري. على سبيل المثال، إذا كان بالإمكان التعرّف على المشاهدة بشكل فريد من خلال نصها،
لا يلزم تحديد أن الملف الشخصي قابل للتعيين أيضًا من
TextView
. بالنسبة للعديد من مشاهدة يجب أن تكون نسبةR.id
للمشاهدة كافية. - إذا كانت طريقة العرض المستهدَفة داخل
AdapterView
، مثلListView
،GridView
أوSpinner
—قد لا تعمل طريقةonView()
. في هذه في هذه الحالة، يجب استخدامonData()
بدلاً من ذلك.
تنفيذ إجراء على إحدى طرق العرض
عند العثور على مطابقة مناسبة للعرض المستهدف، يمكن
إجراء أمثلة من ViewAction
عليها باستخدام طريقة الأداء.
على سبيل المثال، للنقر على طريقة العرض:
Kotlin
onView(...).perform(click())
Java
onView(...).perform(click());
يمكنك تنفيذ أكثر من إجراء واحد باستخدام استدعاء تنفيذ واحد:
Kotlin
onView(...).perform(typeText("Hello"), click())
Java
onView(...).perform(typeText("Hello"), click());
إذا كان الملف الشخصي الذي تتعامل معه يقع داخل ScrollView
(عمودي أو
أفقيًا)، ففكر في الإجراءات السابقة التي تتطلب أن يكون العرض
معروضة، مثل click()
وtypeText()
، باستخدام scrollTo()
هذا النمط
يضمن ظهور طريقة العرض قبل المتابعة إلى الإجراء الآخر:
Kotlin
onView(...).perform(scrollTo(), click())
Java
onView(...).perform(scrollTo(), click());
الاطّلاع على ViewActions
حول إجراءات العرض المقدّمة من Espresso
التحقق من تأكيدات المشاهدات
يمكن تطبيق التأكيدات على طريقة العرض المحدّدة حاليًا باستخدام check()
.
. ويكون التأكيد الأكثر استخدامًا هو التأكيد matches()
. تستخدم
عنصر ViewMatcher
لتأكيد حالة الملف الشخصي المحدّد حاليًا.
على سبيل المثال، للتحقّق من أنّ طريقة العرض تتضمّن النص "Hello!"
:
Kotlin
onView(...).check(matches(withText("Hello!")))
Java
onView(...).check(matches(withText("Hello!")));
إذا أردت تأكيد أنّ "Hello!"
عبارة عن محتوى الملف الشخصي، يُعتبر ما يلي ممارسة سيئة:
Kotlin
// Don't use assertions like withText inside onView. onView(allOf(withId(...), withText("Hello!"))).check(matches(isDisplayed()))
Java
// Don't use assertions like withText inside onView. onView(allOf(withId(...), withText("Hello!"))).check(matches(isDisplayed()));
من ناحية أخرى، إذا أردت تأكيد أنّ الملف الشخصي الذي يتضمّن النص "Hello!"
هو
مرئية - على سبيل المثال بعد تغيير علامة مستوى رؤية المشاهدات -
الرمز جيد.
عرض الاختبار البسيط للتأكيد
في هذا المثال، تحتوي السمة SimpleActivity
على Button
وTextView
. عندما
تم النقر على زر، ويتغير محتوى TextView
إلى "Hello Espresso!"
.
إليك طريقة اختبار ذلك باستخدام قهوة الإسبريسو:
انقر على الزرّ.
تتمثل الخطوة الأولى في البحث عن خاصية تساعد في العثور على الزر. تشير رسالة الأشكال البيانية
في SimpleActivity
يحتوي على R.id
فريد على النحو المتوقع.
Kotlin
onView(withId(R.id.button_simple))
Java
onView(withId(R.id.button_simple));
الآن لإجراء النقر:
Kotlin
onView(withId(R.id.button_simple)).perform(click())
Java
onView(withId(R.id.button_simple)).perform(click());
التحقّق من نص TextView
إنّ السمة TextView
التي تتضمّن النص المطلوب تأكيده تتضمّن سمة R.id
فريدة أيضًا:
Kotlin
onView(withId(R.id.text_simple))
Java
onView(withId(R.id.text_simple));
والآن، للتحقّق من نص المحتوى:
Kotlin
onView(withId(R.id.text_simple)).check(matches(withText("Hello Espresso!")))
Java
onView(withId(R.id.text_simple)).check(matches(withText("Hello Espresso!")));
التأكّد من تحميل البيانات في طرق عرض المحوّلات
AdapterView
هو نوع خاص من التطبيقات المصغّرة التي تحمّل بياناتها ديناميكيًا من
مهايئ. إنّ السمة AdapterView
الأكثر شيوعًا هي ListView
. بالنسبة
بدلاً من الأدوات الثابتة مثل LinearLayout
، مجموعة فرعية فقط من
يمكن تحميل AdapterView
عنصر ثانوي في العرض الهرمي الحالي لطريقة العرض. تطبيق بسيط
لن يعثر بحث onView()
على طرق عرض غير محمَّلة حاليًا.
تتعامل Espresso مع هذا من خلال توفير نقطة دخول onData()
منفصلة وهي
قادر على تحميل عنصر المحول المعني أولاً، مما يجعله موضع التركيز قبل
التي تعمل عليه أو على أي من عناصره الثانوية.
تحذير: عمليات تنفيذ مخصّصة
قد يواجه AdapterView
مشاكل في onData()
إذا خالفوا عقود التوريث، وخاصةً
getItem()
API. في مثل هذه الحالات، يكون الحل الأمثل هو
إعادة ضبط رمز تطبيقك. إذا لم تتمكن من ذلك، يمكنك تنفيذ
قيمة مطابقة AdapterViewProtocol
المخصّصة لمزيد من المعلومات، خذ
يمكنك إلقاء نظرة على
تقدّم Espresso درجة واحدة (AdapterViewProtocols
).
اختبار بسيط لعرض المحوّل
يوضّح هذا الاختبار البسيط كيفية استخدام onData()
. يحتوي SimpleActivity
على
Spinner
مع بعض العناصر التي تمثّل أنواع مشروبات القهوة. عندما يتم
العنصر المحدد، ستتغير قيمة TextView
إلى "One %s a day!"
، حيث
يمثل "%s
" العنصر المحدد.
الهدف من هذا الاختبار هو فتح Spinner
واختيار عنصر معيَّن
تأكَّد من أنّ السمة TextView
تحتوي على العنصر. بما أنّ الفئة Spinner
تستند إلى
فيAdapterView
، من المستحسن استخدام onData()
بدلاً من onView()
تطابق العنصر.
فتح اختيار العنصر
Kotlin
onView(withId(R.id.spinner_simple)).perform(click())
Java
onView(withId(R.id.spinner_simple)).perform(click());
اختيار عنصر
لاختيار العنصر، تنشئ السمة Spinner
ListView
مع محتواها.
يمكن أن تكون طريقة العرض هذه طويلة جدًا، وقد لا يساهم العنصر في العرض.
التسلسل الهرمي. باستخدام onData()
، نضع العنصر المطلوب في العرض
التسلسل الهرمي. العناصر في Spinner
هي سلاسل، لذلك نريد مطابقة عنصر
تساوي السلسلة "Americano"
:
Kotlin
onData(allOf(`is`(instanceOf(String::class.java)), `is`("Americano"))).perform(click())
Java
onData(allOf(is(instanceOf(String.class)), is("Americano"))).perform(click());
التحقق من صحة النص
Kotlin
onView(withId(R.id.spinnertext_simple)) .check(matches(withText(containsString("Americano"))))
Java
onView(withId(R.id.spinnertext_simple)) .check(matches(withText(containsString("Americano"))));
تصحيح الأخطاء
توفّر أداة Espresso معلومات مفيدة حول تصحيح الأخطاء في حال تعذّر إجراء أي اختبار:
التسجيل
تسجِّل Espresso جميع إجراءات العرض إلى Logcat. مثلاً:
ViewInteraction: Performing 'single click' action on view with text: Espresso
العرض الهرمي
تطبع مقهى Espresso العرض الهرمي لطريقة العرض في رسالة الاستثناء عند onView()
فشل.
- إذا لم تعثر
onView()
على العرض المستهدَف، ستكون قيمةNoMatchingViewException
الرمي. يمكنك فحص التدرج الهرمي للعروض في سلسلة الاستثناء لتحليلها سبب عدم تطابق المطابق مع أي مشاهدات. - إذا عثرت
onView()
على عدّة مشاهدات تتطابق مع المُطابق المحدّد، سيتم تم رميAmbiguousViewMatcherException
. تتم طباعة التدرج الهرمي لطريقة العرض تم وضع علامةMATCHES
على الملفات الشخصية التي تمت مطابقتها:
java.lang.RuntimeException: androidx.test.espresso.AmbiguousViewMatcherException This matcher matches multiple views in the hierarchy: (withId: is <123456789>) ... +----->SomeView{id=123456789, res-name=plus_one_standard_ann_button, visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true, window-focus=true, is-focused=false, is-focusable=false, enabled=true, selected=false, is-layout-requested=false, text=, root-is-layout-requested=false, x=0.0, y=625.0, child-count=1} ****MATCHES**** | +------>OtherView{id=123456789, res-name=plus_one_standard_ann_button, visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true, window-focus=true, is-focused=false, is-focusable=true, enabled=true, selected=false, is-layout-requested=false, text=Hello!, root-is-layout-requested=false, x=0.0, y=0.0, child-count=1} ****MATCHES****
عند التعامل مع تسلسل هرمي معقد للعرض أو سلوك غير متوقع للأدوات من المفيد دائمًا استخدام العرض الهرمي في "استوديو Android" على الأجهزة الجوّالة شرح ما.
تحذيرات عرض المحوّل
تحذّر Espresso المستخدمين بشأن توفّر تطبيقات "AdapterView
" المصغّرة. عندما تكون السمة onView()
عملية طرح الأداتين NoMatchingViewException
وAdapterView
الموجودة في التسلسل الهرمي لطريقة العرض، فإن الحل الأكثر شيوعًا هو استخدام onData()
.
ستتضمّن رسالة الاستثناء تحذيرًا يتضمّن قائمة بمشاهدات المحوّل.
ويمكنك استخدام هذه المعلومات لاستدعاء onData()
لتحميل طريقة العرض المستهدَفة.
مصادر إضافية
لمزيد من المعلومات حول استخدام قهوة الإسبريسو في اختبارات Android، يُرجى الرجوع إلى الموارد التالية.
نماذج
- CustomMatcherSample:
لعرض كيفية توسيع نطاق Espresso لمطابقة خاصية التلميح لكائن
EditText
. - RecyclerViewSample:
RecyclerView
إجراء لـ Espresso - (المزيد...)