أساسيات الإسبريسو

يوضح هذا المستند كيفية إكمال مهام الاختبار التلقائي الشائعة باستخدام واجهة برمجة تطبيقات 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، يُرجى الرجوع إلى الموارد التالية.

نماذج