يوفّر إطار عمل اختبار UI Automator مجموعة من واجهات برمجة التطبيقات لإنشاء اختبارات واجهة المستخدم التي تتفاعل مع تطبيقات المستخدم وتطبيقات النظام.
مقدمة عن اختبار UI Automator الحديث
يقدّم UI Automator 2.4 لغة خاصة بالنطاق (DSL) مبسطة ومتوافقة مع Kotlin تسهّل كتابة اختبارات واجهة المستخدم لتطبيق Android. تركز واجهة برمجة التطبيقات الجديدة هذه على العثور على العناصر المستندة إلى المسندات والتحكّم الصريح في حالات التطبيق. يمكنك استخدامها لإنشاء اختبارات تلقائية أكثر سهولة في الصيانة وموثوقية.
يتيح لك UI Automator اختبار تطبيق من خارج عملية التطبيق. يتيح لك ذلك اختبار إصدارات الإصدار مع تطبيق التصغير. يساعد UI Automator أيضًا عند كتابة اختبارات قياس الأداء على نطاق واسع.
تشمل الميزات الرئيسية للطريقة الحديثة ما يلي:
- نطاق اختبار
uiAutomatorمخصّص لرمز اختبار أكثر وضوحًا وتعبيراً - طُرق مثل
onElementوonElementsوonElementOrNullللعثور على عناصر واجهة المستخدم باستخدام مسندات واضحة - آلية انتظار مضمّنة للعناصر الشرطية
onElement*(timeoutMs: Long = 10000) - إدارة صريحة لحالة التطبيق، مثل
waitForStableوwaitForAppToBeVisible - تفاعل مباشر مع عُقد نافذة تسهيل الاستخدام لسيناريوهات اختبار النوافذ المتعددة
- إمكانات مضمّنة لأخذ لقطات الشاشة و
ResultsReporterللاختبار المرئي وتصحيح الأخطاء
إعداد المشروع
لبدء استخدام واجهات برمجة تطبيقات UI Automator الحديثة، عدِّل ملف مشروعك
build.gradle.kts لتضمين أحدث اعتمادية:
Kotlin
dependencies {
...
androidTestImplementation("androidx.test.uiautomator:uiautomator:2.4.0-alpha05")
}
أنيق
dependencies {
...
androidTestImplementation "androidx.test.uiautomator:uiautomator:2.4.0-alpha05"
}
المفاهيم الأساسية لواجهة برمجة التطبيقات
توضّح الأقسام التالية المفاهيم الأساسية لواجهة برمجة تطبيقات UI Automator الحديثة.
نطاق اختبار uiAutomator
يمكنك الوصول إلى جميع واجهات برمجة تطبيقات UI Automator الجديدة ضمن البلوك uiAutomator { ... }. تنشئ هذه الدالة UiAutomatorTestScope يوفّر بيئة موجزة وآمنة من حيث النوع لعمليات الاختبار.
uiAutomator {
// All your UI Automator actions go here
startApp("com.example.targetapp")
onElement { textAsString() == "Hello, World!" }.click()
}
العثور على عناصر واجهة المستخدم
يمكنك استخدام واجهات برمجة تطبيقات UI Automator مع المسندات لتحديد موقع عناصر واجهة المستخدم. تتيح لك هذه المسندات تحديد شروط للسمات، مثل النص والحالة المحدّدة أو الحالة التي تم التركيز عليها ووصف المحتوى.
onElement { predicate }: تعرض أول عنصر في واجهة المستخدم يطابق المسند خلال مهلة تلقائية. تعرض الدالة استثناءً إذا لم تعثر على عنصر مطابق.// Find a button with the text "Submit" and click it onElement { textAsString() == "Submit" }.click() // Find a UI element by its resource ID onElement { viewIdResourceName == "my_button_id" }.click() // Allow a permission request watchFor(PermissionDialog) { clickAllow() }onElementOrNull { predicate }: تشبهonElement، ولكنها تعرضnullإذا لم تعثر الدالة على أي عنصر مطابق خلال المهلة. لا تعرض هذه الدالة استثناءً. استخدِم هذه الطريقة للعناصر الاختيارية.val optionalButton = onElementOrNull { textAsString() == "Skip" } optionalButton?.click() // Click only if the button existsonElements { predicate }: تنتظر هذه الدالة إلى أن يطابق عنصر واحد على الأقل في واجهة المستخدم المسند المحدّد، ثم تعرض قائمة بجميع عناصر واجهة المستخدم المطابقة.// Get all items in a list Ui element val listItems = onElements { className == "android.widget.TextView" && isClickable } listItems.forEach { it.click() }
في ما يلي بعض النصائح لاستخدام طلبات onElement:
ربط طلبات
onElementللعناصر المتداخلة: يمكنك ربط طلباتonElementللعثور على عناصر داخل عناصر أخرى، باتّباع تسلسل هرمي بين العنصر الرئيسي والعنصر الثانوي.// Find a parent Ui element with ID "first", then its child with ID "second", // then its grandchild with ID "third", and click it. onElement { viewIdResourceName == "first" } .onElement { viewIdResourceName == "second" } .onElement { viewIdResourceName == "third" } .click()حدِّد مهلة لوظائف
onElement*من خلال تمرير قيمة تمثّل الملّي ثانية.// Find a Ui element with a zero timeout (instant check) onElement(0) { viewIdResourceName == "something" }.click() // Find a Ui element with a custom timeout of 10 seconds onElement(10_000) { textAsString() == "Long loading text" }.click()
التفاعل مع عناصر واجهة المستخدم
يمكنك التفاعل مع عناصر واجهة المستخدم من خلال محاكاة النقرات أو ضبط النص في الحقول القابلة للتعديل.
// Click a Ui element
onElement { textAsString() == "Tap Me" }.click()
// Set text in an editable field
onElement { className == "android.widget.EditText" }.setText("My input text")
// Perform a long click
onElement { contentDescription == "Context Menu" }.longClick()
التعامل مع حالات التطبيق والمراقِبين
يمكنك إدارة دورة حياة تطبيقك والتعامل مع عناصر واجهة المستخدم غير المتوقّعة التي قد تظهر أثناء الاختبارات.
إدارة دورة حياة التطبيق
توفّر واجهات برمجة التطبيقات طرقًا للتحكّم في حالة التطبيق قيد الاختبار:
// Start a specific app by package name. Used for benchmarking and other
// self-instrumenting tests.
startApp("com.example.targetapp")
// Start a specific activity within the target app
startActivity(SomeActivity::class.java)
// Start an intent
startIntent(myIntent)
// Clear the app's data (resets it to a fresh state)
clearAppData("com.example.targetapp")
التعامل مع واجهة المستخدم غير المتوقّعة
تتيح لك واجهة برمجة التطبيقات watchFor تحديد معالِجات لعناصر واجهة المستخدم غير المتوقّعة، مثل مربّعات حوار الأذونات، التي قد تظهر أثناء سير الاختبار. تستخدم هذه الدالة آلية المراقبة الداخلية، ولكنها توفّر مرونة أكبر.
import androidx.test.uiautomator.PermissionDialog
@Test
fun myTestWithPermissionHandling() = uiAutomator {
startActivity(MainActivity::class.java)
// Register a watcher to click "Allow" if a permission dialog appears
watchFor(PermissionDialog) { clickAllow() }
// Your test steps that might trigger a permission dialog
onElement { textAsString() == "Request Permissions" }.click()
// Example: You can register a different watcher later if needed
clearAppData("com.example.targetapp")
// Now deny permissions
startApp("com.example.targetapp")
watchFor(PermissionDialog) { clickDeny() }
onElement { textAsString() == "Request Permissions" }.click()
}
PermissionDialog هو مثال على ScopedWatcher<T>، حيث T هو الكائن الذي يتم تمريره كنطاق إلى البلوك في watchFor. يمكنك إنشاء مراقِبين مخصّصين استنادًا إلى هذا النمط.
الانتظار إلى أن يصبح التطبيق مرئيًا ومستقرًا
تحتاج الاختبارات أحيانًا إلى الانتظار إلى أن تصبح العناصر مرئية أو مستقرة. يوفّر UI Automator عدة واجهات برمجة تطبيقات للمساعدة في ذلك.
تنتظر الدالة waitForAppToBeVisible("com.example.targetapp") إلى أن يظهر على الشاشة عنصر في واجهة المستخدم بالاسم المحدد للحزمة خلال مهلة قابلة للتخصيص.
// Wait for the app to be visible after launching it
startApp("com.example.targetapp")
waitForAppToBeVisible("com.example.targetapp")
استخدِم واجهة برمجة التطبيقات waitForStable() للتأكّد من أنّ واجهة مستخدم التطبيق تُعتبر مستقرة قبل التفاعل معها.
// Wait for the entire active window to become stable
activeWindow().waitForStable()
// Wait for a specific Ui element to become stable (e.g., after a loading animation)
onElement { viewIdResourceName == "my_loading_indicator" }.waitForStable()
استخدام UI Automator لقياس الأداء على نطاق واسع وإنشاء الملفات الشخصية الأساسية
يمكنك استخدام UI Automator لاختبار الأداء باستخدام Jetpack Macrobenchmark ولإنشاء الملفات الشخصية الأساسية، لأنّه يوفّر طريقة موثوقة للتفاعل مع تطبيقك وقياس الأداء من منظور المستخدم النهائي.
يستخدم Macrobenchmark واجهات برمجة تطبيقات UI Automator لتشغيل واجهة المستخدم وقياس التفاعلات.
على سبيل المثال، في قياسات الأداء عند بدء التشغيل، يمكنك استخدام onElement لرصد وقت تحميل محتوى واجهة المستخدم بالكامل، ما يتيح لك قياس الوقت اللازم للعرض الكامل
(TTFD). في قياسات الأداء المتعلقة بإيقاف مؤقت لعرض واجهة المستخدم، يتم استخدام واجهات برمجة تطبيقات UI Automator للتنقّل في القوائم أو تشغيل الصور المتحركة لقياس أوقات عرض اللقطة. تكون الدوال مثل startActivity() أو startIntent() مفيدة لوضع التطبيق في الحالة الصحيحة قبل بدء القياس.
عند إنشاء الملفات الشخصية الأساسية، يمكنك برمجة رحلات المستخدمين الأساسية (CUJ) في تطبيقك لتسجيل الفئات والطُرق التي تتطلب التجميع المسبق. يُعدّ UI Automator أداة مثالية لكتابة نصوص البرمجة التلقائية هذه. يؤدي العثور على العناصر المستندة إلى المسندات وآليات الانتظار المضمّنة (onElement) في لغة DSL الحديثة إلى تنفيذ اختبار تجريبي أكثر قوةً وتحديدًا مقارنةً بالطُرق الأخرى.
يقلّل هذا الاستقرار من عدم الثبات ويضمن أنّ الملف الشخصي للمرجع الذي تم إنشاؤه يعكس بدقة المسارات البرمجية التي يتم تنفيذها أثناء أهم مسارات المستخدم.
الميزات المتقدمة
تكون الميزات التالية مفيدة لسيناريوهات الاختبار الأكثر تعقيدًا.
التفاعل مع نوافذ متعددة
تتيح لك واجهات برمجة تطبيقات UI Automator التفاعل مباشرةً مع عناصر واجهة المستخدم وفحصها. يكون ذلك مفيدًا بشكل خاص في السيناريوهات التي تتضمّن نوافذ متعددة، مثل وضع "صورة داخل صورة" (PiP) أو تنسيقات الشاشة المقسّمة.
// Find the first window that is in Picture-in-Picture mode
val pipWindow = windows()
.first { it.isInPictureInPictureMode == true }
// Now you can interact with elements within that specific window
pipWindow.onElement { textAsString() == "Play" }.click()
لقطات الشاشة والتأكيدات المرئية
يمكنك أخذ لقطات شاشة للشاشة بأكملها أو نوافذ معيّنة أو عناصر فردية في واجهة المستخدم مباشرةً ضمن اختباراتك. يساعد ذلك في اختبار الانحدار المرئي وتصحيح الأخطاء.
uiautomator {
// Take a screenshot of the entire active window
val fullScreenBitmap: Bitmap = activeWindow().takeScreenshot()
fullScreenBitmap.saveToFile(File("/sdcard/Download/full_screen.png"))
// Take a screenshot of a specific UI element (e.g., a button)
val buttonBitmap: Bitmap = onElement { viewIdResourceName == "my_button" }.takeScreenshot()
buttonBitmap.saveToFile(File("/sdcard/Download/my_button_screenshot.png"))
// Example: Take a screenshot of a PiP window
val pipWindowScreenshot = windows()
.first { it.isInPictureInPictureMode == true }
.takeScreenshot()
pipWindowScreenshot.saveToFile(File("/sdcard/Download/pip_screenshot.png"))
}
تسهّل دالة الإضافة saveToFile لـ Bitmap حفظ الصورة التي تم التقاطها في مسار محدّد.
استخدام ResultsReporter لتصحيح الأخطاء
يساعدك ResultsReporter في ربط نتائج الاختبارات، مثل لقطات الشاشة، مباشرةً بنتائج الاختبارات في "استوديو Android" لتسهيل الفحص وتصحيح الأخطاء.
uiAutomator {
startApp("com.example.targetapp")
val reporter = ResultsReporter("MyTestArtifacts") // Name for this set of results
val file = reporter.addNewFile(
filename = "my_screenshot",
title = "Accessible button image" // Title that appears in Android Studio test results
)
// Take a screenshot of an element and save it using the reporter
onElement { textAsString() == "Accessible button" }
.takeScreenshot()
.saveToFile(file)
// Report the artifacts to instrumentation, making them visible in Android Studio
reporter.reportToInstrumentation()
}
الترحيل من إصدارات UI Automator الأقدم
إذا كانت لديك اختبارات UI Automator حالية مكتوبة باستخدام واجهات برمجة تطبيقات أقدم، استخدِم الجدول التالي كمرجع للترحيل إلى الطريقة الحديثة:
| نوع الإجراء | طريقة UI Automator القديمة | طريقة UI Automator الجديدة |
|---|---|---|
| نقطة الإدخال | UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) |
يمكنك تضمين منطق الاختبار في نطاق uiAutomator { ... }. |
| العثور على عناصر واجهة المستخدم | device.findObject(By.res("com.example.app:id/my_button")) |
onElement { viewIdResourceName == "my\_button" } |
| العثور على عناصر واجهة المستخدم | device.findObject(By.text("Click Me")) |
onElement { textAsString() == "Click Me" } |
| الانتظار إلى أن تصبح واجهة المستخدم غير نشطة | device.waitForIdle() |
ننصح باستخدام آلية المهلة المضمّنة في onElement، وإلا استخدِم activeWindow().waitForStable() |
| العثور على العناصر الثانوية | طلبات findObject المتداخلة يدويًا |
ربط onElement().onElement() |
| التعامل مع مربّعات حوار الأذونات | UiAutomator.registerWatcher() |
watchFor(PermissionDialog) |