Espresso هو إطار عمل لاختبار واجهة المستخدم الآلي لتطبيقات Android، طورته فريق Google وهو جزء من AndroidX Test. على عكس الاختبارات الآلية التي تتحقق من المكونات المعزولة، يتفاعل Espresso مع واجهة المستخدم الحقيقية: ينقر على الأزرار، يدخل النص، ويتحقق من عرض العناصر. وفقًا لـ Google Android Developers، يوفر Espresso مزامنة تلقائية مع سلسلة واجهة المستخدم، مما يلغي الحاجة إلى Thread.sleep() اليدوي.
الخلاصة
Espresso هي مكتبة لكتابة اختبارات واجهة مستخدم آلية لـ Android، وهي جزء من Google AndroidX Test. توفر واجهة برمجة تطبيقات للعثور على عناصر View على الشاشة، وتنفيذ إجراءات عليها (نقر، إدخال، تمرير)، والتحقق من حالتها (معروض، يحتوي على نص، ممكّن).
الميزة الرئيسية لـ Espresso هي المزامنة التلقائية مع السلسلة الرئيسية للتطبيق. ينتظر الإطار اكتمال جميع المهام غير المتزامنة (coroutines، AsyncTask، Handler) قبل تنفيذ الفحص التالي. يلغي هذا الاختبارات غير المستقرة الناتجة عن حالات السباق ويجعل اختبارات واجهة المستخدم مستقرة وموثوقة — لا يحتوي أي اختبار على Thread.sleep() أو حلقات انتظار.
يتبع Espresso مبدأ الكلب ذو الثلاث أرجل — يتكون الاختبار من ثلاث خطوات: العثور على عنصر (ViewMatcher)، تنفيذ إجراء (ViewAction)، التحقق من النتيجة (ViewAssertion). تُكتب الخطوات الثلاث في سلسلة استدعاءات: onView().perform().check(). هذا المفهوم يجعل الاختبارات قابلة للتنبؤ وسهلة القراءة — يصف كل اختبار بوضوح ما يبحث عنه، وما يفعله، وما يتحقق منه.
بنية Espresso تعتمد على ثلاثة مكونات: Espresso (نقطة الدخول — الأساليب الثابتة onView و onData)، ViewMatchers (البحث عن العناصر)، ViewActions (الإجراءات) و ViewAssertions (التحقق). داخليًا، يستخدم الإطار Idling Resource للمزامنة مع سلسلة واجهة المستخدم.
أبسط اختبار يجد زرًا بواسطة ID، ينقر عليه، ويتحقق من ظهور النص «تم». جميع العمليات متزامنة من منظور الاختبار — يضمن Espresso أن سلسلة واجهة المستخدم قد انتهت من معالجة الحدث قبل أن يستمر الاختبار. يتم تحقيق ذلك من خلال آلية انتظار مدمجة: onView يمنع تنفيذ الاختبار حتى تصبح واجهة المستخدم خاملة.
@Test
fun buttonClick_showsSuccessText() {
// البحث عن زر بواسطة ID والنقر
onView(withId(R.id.button_submit))
.perform(click())
// التحقق من عرض النص «تم»
onView(withText("تم"))
.check(matches(isDisplayed()))
}
لإطلاق اختبار Espresso، يُستخدم ActivityScenario (AndroidX Test)، الذي ينشئ Activity في حالة محددة — قيد التشغيل، متوقفة، أو مدمرة. يسمح ActivityScenario باختبار دورة حياة Activity بالإضافة إلى اختبار واجهة المستخدم الخالص. على سبيل المثال، يمكن التحقق من الحفاظ على البيانات عند تدوير الشاشة (إعادة إنشاء Activity) واستعادتها بعد التدمير.
ViewMatchers هي مجموعة من الأساليب من فئة Espresso.onView التي تسمح بالعثور على Views على الشاشة بمعايير مختلفة: ID المورد (R.id)، النص، التلميح، العنصر الأب، والتسلسل الهرمي. إذا لم يعطِ matcher واحد نتيجة فريدة، يمكن دمج matchers باستخدام allOf().
| المطابق | الغرض |
|---|---|
| withId(R.id.name) | البحث بواسطة ID المورد |
| withText(«نص») | البحث بواسطة النص المعروض |
| withHint(«تلميح») | البحث بواسطة خاصية التلميح لـ EditText |
| isDisplayed() | التحقق من أن العنصر مرئي على الشاشة |
| hasSibling(matcher) | البحث بواسطة العنصر المجاور |
| allOf(m1, m2) | دمج عدة مطابقات |
إذا كان هناك عدة عناصر متطابقة على الشاشة (مثل TextView بنصوص مختلفة)، فمن المناسب دمج المطابقات باستخدام allOf: onView(allOf(withId(R.id.title), withText(«مرحبًا»))). يضمن ذلك تحديد عنصر واحد. العامل العكسي — not() — يستبعد العناصر من البحث، و hasSibling() يجد عنصرًا بجوار عنصر معروف.
ViewActions هي إجراءات ينفذها Espresso على View الموجود: click(), typeText(), clearText(), scrollTo(), swipeLeft() وغيرها. تُمرر الإجراءات إلى طريقة perform()، التي يمكنها قبول إجراءات متعددة بالتسلسل.
طريقة perform() تقبل vararg من ViewAction، مما يسمح بتنفيذ سلسلة من الإجراءات على عنصر واحد: مسح الحقل، إدخال نص جديد، إغلاق لوحة المفاتيح، والنقر على زر. تُنفذ جميع الإجراءات بالترتيب المذكور، ويضمن Espresso اكتمال الإجراء السابق قبل بدء التالي.
// إدخال نص في EditText والنقر على زر
onView(withId(R.id.edit_email))
.perform(
clearText(),
typeText("user@example.com"),
closeSoftKeyboard()
)
onView(withId(R.id.button_login))
.perform(click())
للعثور على عناصر داخل AdapterView (ListView, RecyclerView)، يُستخدم أسلوب onData() بدلاً من onView. يعمل مع بيانات المحول بدلاً من Views — يجد عنصرًا بمحتوى النموذج ويعيد View المقابلة لإجراءات إضافية. يستخدم onData مطابقات hamcrest لتحديد موقع عنصر حسب حقول بيانات النموذج.
ViewAssertions تتحقق من أن View في حالة محددة. الطريقة الأساسية — matches(matcher) — تتحقق من تطابق العنصر مع المطابق المحدد. بالإضافة إلى ذلك، يقدم Espresso doesNotExist() (العنصر غير موجود) و selectedDescendantsMatch() (التحقق من العناصر المتداخلة).
الفحوصات الأكثر شيوعًا في اختبارات واجهة المستخدم: العنصر معروض (isDisplayed)، العنصر يحتوي على نص محدد (withText)، العنصر ممكّن (isEnabled)، العنصر غير محدد (isNotChecked). كل فحص يرمي استثناءً مفصلاً في حالة الفشل — بما في ذلك التسلسل الهرمي للـ Views على الشاشة. هذا يبسط التصحيح: تظهر رسالة الخطأ العناصر التي كانت فعليًا على الشاشة في وقت الفحص.
إذا كانت الفحوصات القياسية غير كافية، يمكن إنشاء فحص مخصص من خلال واجهة ViewAssertion. التأكيد المخصص يستقبل View ويمكنه التحقق من حالته برمجيًا — على سبيل المثال، لون النص، الهوامش، أو حالة مكون مخصص غير مكشوف من خلال المطابقات القياسية.
// تحقق: TextView معروض ويحتوي على نص
onView(withId(R.id.text_welcome))
.check(matches(isDisplayed()))
.check(matches(withText("مرحبًا")))
// تحقق: العنصر غير معروض
onView(withId(R.id.progress_bar))
.check(doesNotExist())
Idling Resource هي آلية Espresso لمزامنة الاختبار مع العمليات غير المتزامنة. افتراضيًا، ينتظر Espresso Handler و AsyncTask و coroutines (عبر coroutinesIdlingResource). إذا كان التطبيق يقوم بعمل خلفي من خلال سلاسل مخصصة أو خدمات رد اتصال، يجب تسجيل Idling Resource مخصص.
بدءًا من AndroidX Test 1.4.0، يدعم Espresso coroutines عبر CoroutinesIdlingResource. ينتظر الاختبار تلقائيًا اكتمال جميع coroutines المُطلقة قبل إجراء فحوصات واجهة المستخدم. للسيناريوهات الأكثر تعقيدًا، يُستخدم CountingIdlingResource — عداد يزداد عند بدء المهمة وينخفض عند اكتمالها.
// تسجيل IdlingResource لـ OkHttp
class OkHttpIdlingResource(
private val client: OkHttpClient
) : IdlingResource {
private var isIdle = true
private var watcher: IdlingResource.ResourceCallback? = null
override fun getName() = "OkHttp"
override fun isIdleNow() = isIdle
override fun registerIdleTransitionCallback(
callback: IdlingResource.ResourceCallback
) {
watcher = callback
}
}
توصيل Espresso في مشروع Android يتم بإضافة التبعيات إلى ملف build.gradle على مستوى الوحدة. Espresso جزء من AndroidX Test، لذا يكفي تحديد تبعيات نواة Espresso والإضافات وتكامل JUnit. توضع الاختبارات في دليل src/androidTest وتُشغّل على جهاز فعلي أو محاكي عبر AndroidJUnitRunner.
مجموعة التبعيات الدنيا تشمل espresso-core (النواة)، espresso-contrib (مطابقات إضافية لـ RecyclerView و Drawer و Picker) و runner (مشغل اختبارات AndroidX). تُشغّل جميع الاختبارات على محاكي أو جهاز فعلي عبر Android Test Orchestrator.
// build.gradle.kts (تبعيات androidTest)
android {
defaultConfig {
testInstrumentationRunner =
"androidx.test.runner.AndroidJUnitRunner"
}
}
dependencies {
androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1")
androidTestImplementation("androidx.test.espresso:espresso-contrib:3.6.1")
androidTestImplementation("androidx.test:runner:1.6.1")
androidTestImplementation("androidx.test:rules:1.6.1")
}
يمكن تشغيل اختبارات Espresso عبر Google Android Test Orchestrator، الذي يعزل كل اختبار في عملية منفصلة وينظف الحالة بين عمليات التشغيل. يلغي هذا الاختبارات غير المستقرة المرتبطة بالبيانات المتبقية من الاختبارات السابقة ويعزز الاستقرار على خوادم CI. للتشغيل المتوازي، يُستخدم sharding — توزيع الاختبارات بين عدة محاكيات.
الأسئلة الشائعة
Espresso يعمل داخل عملية التطبيق ويستخدم مزامنة تلقائية مع سلسلة واجهة المستخدم. يعمل UI Automator على مستوى النظام، ويمكنه التفاعل مع التطبيقات الأخرى، لكنه يتطلب إدارة انتظار يدوية.
هذا استعارة من عرض Google التقديمي: اختبار Espresso يقف على ثلاث دعائم — ViewMatcher (بحث)، ViewAction (إجراء) و ViewAssertion (تحقق). إزالة أي منها تجعل الاختبار غير مستقر، مثل كلب بثلاث أرجل.
بالنسبة لـ RecyclerView، تُستخدم مكتبة espresso-contrib وطرق مثل onView(withId(R.id.recycler)).perform(actionOnItemAtPosition(0, click())). البديل هو onData() لـ AdapterView أو ViewAction مخصص للبحث عن عنصر بنص داخل RecyclerView. بالإضافة إلى ذلك، يمكن استخدام RecyclerViewActions من espresso-contrib للتمرير إلى عنصر وتنفيذ إجراءات عليه.
الاختبار غير المستقر — اختبار يفشل أحيانًا دون تغيير في الكود، بسبب حالات السباق أو عدم التزامن. يحل Espresso هذه المشكلة عبر Idling Resource — انتظار اكتمال جميع المهام الخلفية قبل تنفيذ الفحص.
Espresso نفسه غير مصمم لاختبارات لقطات الشاشة، لكن يمكن دمجه مع مكتبات مثل Shot أو Paparazzi. يحضر Espresso واجهة المستخدم في الحالة المطلوبة، وتأخذ مكتبة المقارنة لقطة شاشة وتقارنها بالمرجع. يُسمى هذا النهج اختبار الانحدار البصري ويساعد في العثور على تغييرات غير متوقعة في الواجهة.
الملخص
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.
اقرأ أيضًا