چارچوب تست UI Automator مجموعهای از APIها را برای ساخت تستهای UI ارائه میکند که با برنامههای کاربر و برنامههای سیستم تعامل دارند.
مقدمه ای بر تست مدرن UI Automator
UI Automator 2.4 یک زبان خاص دامنه (DSL) کارآمد و سازگار با Kotlin را معرفی می کند که نوشتن تست های رابط کاربری را برای اندروید ساده می کند. این سطح API جدید بر یافتن عنصر مبتنی بر گزاره و کنترل صریح بر روی حالات برنامه تمرکز دارد. از آن برای ایجاد تست های خودکار قابل نگهداری و قابل اعتمادتر استفاده کنید.
UI Automator به شما امکان می دهد یک برنامه را از خارج از فرآیند برنامه آزمایش کنید. این به شما امکان میدهد نسخههای منتشر شده را با کوچکسازی اعمال شده آزمایش کنید. UI Automator همچنین هنگام نوشتن تست های ماکرو بنچمارک کمک می کند.
ویژگی های کلیدی رویکرد مدرن عبارتند از:
- یک محدوده آزمایشی اختصاصی
uiAutomator
برای کد تست تمیزتر و گویاتر. - روشهایی مانند
onElement
،onElements
وonElementOrNull
برای یافتن عناصر UI با محمولهای واضح. - مکانیزم انتظار داخلی برای عناصر شرطی
onElement*(timeoutMs: Long = 10000)
- مدیریت وضعیت برنامه صریح مانند
waitForStable
وwaitForAppToBeVisible
. - تعامل مستقیم با گره های پنجره دسترسی برای سناریوهای آزمایش چند پنجره ای.
- قابلیت های اسکرین شات داخلی و
ResultsReporter
برای تست بصری و اشکال زدایی.
پروژه خود را تنظیم کنید
برای شروع استفاده از APIهای مدرن UI Automator، فایل build.gradle.kts
پروژه خود را بهروزرسانی کنید تا آخرین وابستگی را در خود داشته باشد:
کاتلین
dependencies {
...
androidTestImplementation("androidx.test.uiautomator:uiautomator:2.4.0-alpha05")
}
شیار
dependencies {
...
androidTestImplementation "androidx.test.uiautomator:uiautomator:2.4.0-alpha05"
}
مفاهیم اصلی API
بخشهای زیر مفاهیم اصلی API مدرن UI Automator را شرح میدهند.
محدوده تست uiAutomator
به تمام APIهای جدید UI Automator در بلوک uiAutomator { ... }
دسترسی داشته باشید. این تابع یک UiAutomatorTestScope
ایجاد می کند که یک محیط مختصر و ایمن برای عملیات آزمایشی شما فراهم می کند.
uiAutomator {
// All your UI Automator actions go here
startApp("com.example.targetapp")
onElement { textAsString() == "Hello, World!" }.click()
}
عناصر UI را پیدا کنید
برای مکان یابی عناصر UI از API های 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 }
: صبر می کند تا حداقل یک عنصر UI با محمول داده شده مطابقت داشته باشد، سپس لیستی از همه عناصر UI منطبق را برمی گرداند.// Get all items in a list Ui element val listItems = onElements { className == "android.widget.TextView" && isClickable } listItems.forEach { it.click() }
در اینجا چند نکته برای استفاده از تماس های onElement
وجود دارد:
Chain
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()
تعامل با عناصر UI
با شبیه سازی کلیک ها یا تنظیم متن در فیلدهای قابل ویرایش، با عناصر UI تعامل کنید.
// 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()
وضعیت برنامه ها و ناظران را مدیریت کنید
چرخه عمر برنامهتان را مدیریت کنید و عناصر رابط کاربری غیرمنتظرهای را که ممکن است در طول آزمایشهایتان ظاهر شوند، مدیریت کنید.
مدیریت چرخه عمر اپلیکیشن
API ها راه هایی برای کنترل وضعیت برنامه تحت آزمایش ارائه می دهند:
// 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
API به شما امکان میدهد برای عناصر غیرمنتظره UI، مانند گفتگوهای مجوز، که ممکن است در جریان آزمایش شما ظاهر شوند، کنترلکنندههایی تعریف کنید. این از مکانیزم ناظر داخلی استفاده می کند اما انعطاف پذیری بیشتری را ارائه می دهد.
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 چندین API را برای کمک به این امر ارائه می دهد.
waitForAppToBeVisible("com.example.targetapp")
منتظر می ماند تا عنصر UI با نام بسته داده شده در یک بازه زمانی قابل تنظیم روی صفحه ظاهر شود.
// Wait for the app to be visible after launching it
startApp("com.example.targetapp")
waitForAppToBeVisible("com.example.targetapp")
از waitForStable()
API استفاده کنید تا مطمئن شوید که رابط کاربری برنامه قبل از تعامل با آن پایدار است.
// 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()
ویژگی های پیشرفته
ویژگی های زیر برای سناریوهای آزمایش پیچیده تر مفید هستند.
تعامل با چندین ویندوز
APIهای UI Automator به شما امکان می دهند مستقیماً با عناصر UI تعامل داشته باشید و آنها را بررسی کنید. این به ویژه برای سناریوهایی که شامل چندین پنجره است، مانند حالت تصویر در تصویر (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()
اسکرین شات ها و ادعاهای بصری
از کل صفحه، پنجرههای خاص، یا عناصر UI مجزا مستقیماً در آزمایشهای خود اسکرینشات بگیرید. این برای تست رگرسیون بصری و اشکال زدایی مفید است.
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 Studio مرتبط کنید تا بازرسی و اشکالزدایی آسانتر را انجام دهید.
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 موجود با سطوح API قدیمیتر نوشته شدهاند، از جدول زیر به عنوان مرجع برای مهاجرت به رویکرد مدرن استفاده کنید:
نوع عمل | روش قدیمی UI Automator | روش جدید UI Automator |
---|---|---|
نقطه ورود | UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) | منطق تست را در محدوده uiAutomator { ... } قرار دهید. |
عناصر UI را پیدا کنید | device.findObject(By.res("com.example.app:id/my_button")) | onElement { id == "my\_button" } |
عناصر UI را پیدا کنید | device.findObject(By.text("Click Me")) | onElement { textAsString() == "Click Me" } |
منتظر UI غیرفعال باشید | device.waitForIdle() | مکانیسم وقفه داخلی داخلی onElement را ترجیح دهید. در غیر این صورت، activeWindow().waitForStable() |
عناصر کودک را پیدا کنید | تماسهای findObject تودرتوی دستی | زنجیربندی onElement().onElement() |
مدیریت گفتگوهای مجوز | UiAutomator.registerWatcher() | watchFor(PermissionDialog) |