يوفّر إطار عمل اختبار UI Automator مجموعة من واجهات برمجة التطبيقات لإنشاء اختبارات واجهة المستخدم التي تتفاعل مع تطبيقات المستخدمين وتطبيقات النظام.
مقدمة عن اختبارات UI Automator الحديثة
توفّر الإصدار 2.4 من UI Automator لغة خاصة بالمجال (DSL) مبسطة ومتوافقة مع Kotlin، ما يسهّل كتابة اختبارات واجهة المستخدم على Android. يركّز سطح واجهة برمجة التطبيقات الجديد هذا على العثور على العناصر المستندة إلى المسند والتحكّم الصريح في حالات التطبيق. يمكنك استخدامها لإنشاء اختبارات مبرمَجة أكثر موثوقية وسهولة في الصيانة.
تتيح لك أداة UI Automator اختبار تطبيق من خارج عملية التطبيق. يتيح لك ذلك اختبار إصدارات التطبيق مع تطبيق التصغير. تساعد أداة UI Automator أيضًا في كتابة اختبارات Macrobenchmark.
تشمل الميزات الرئيسية للأسلوب الحديث ما يلي:
- نطاق اختبار مخصّص
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")
}
Groovy
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 { id == "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 exists
onElements { 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 { id == "first" } .onElement { id == "second" } .onElement { id == "third" } .click()
حدِّد مهلة لوظائف
onElement*
من خلال تمرير قيمة تمثّل ملّي ثانية.// Find a Ui element with a zero timeout (instant check) onElement(0) { id == "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 { id == "my_loading_indicator" }.waitForStable()
الميزات المتقدمة
الميزات التالية مفيدة في سيناريوهات الاختبار الأكثر تعقيدًا.
التفاعل مع نوافذ متعددة
تتيح لك واجهات برمجة التطبيقات UI Automator التفاعل مباشرةً مع عناصر واجهة المستخدم وفحصها. ويكون ذلك مفيدًا بشكل خاص في السيناريوهات التي تتضمّن نوافذ متعددة، مثل وضع "نافذة ضمن النافذة" أو تنسيقات تقسيم الشاشة.
// 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 { id == "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 { id == "my\_button" } |
العثور على عناصر واجهة المستخدم | device.findObject(By.text("Click Me")) |
onElement { textAsString() == "Click Me" } |
انتظار واجهة المستخدم غير النشطة | device.waitForIdle() |
استخدام آلية المهلة المضمّنة في onElement ، وإلا activeWindow().waitForStable() |
العثور على العناصر الفرعية | المكالمات المتداخلة findObject يدويًا |
onElement().onElement() الربط |
التعامل مع مربّعات حوار الأذونات | UiAutomator.registerWatcher() |
watchFor(PermissionDialog) |