เฟรมเวิร์กการทดสอบ UI Automator มีชุด API สำหรับสร้างการทดสอบ UI ที่โต้ตอบกับแอปของผู้ใช้และแอปของระบบ
ข้อมูลเบื้องต้นเกี่ยวกับการทดสอบ UI Automator สมัยใหม่
UI Automator 2.4 ขอแนะนำ Domain Specific Language (DSL) ที่ปรับปรุงแล้วและเป็นมิตรกับ Kotlin ซึ่งช่วยให้การเขียนการทดสอบ UI สำหรับ Android ง่ายขึ้น API ใหม่ นี้มุ่งเน้นการค้นหาองค์ประกอบตามตัวบ่งชี้และการควบคุมสถานะแอปอย่างชัดเจน ใช้เพื่อสร้างการทดสอบอัตโนมัติที่ดูแลรักษาได้ง่ายและเชื่อถือได้มากขึ้น
UI Automator ช่วยให้คุณทดสอบแอปจากภายนอกกระบวนการของแอปได้ ซึ่งจะช่วยให้คุณทดสอบเวอร์ชันที่เผยแพร่โดยใช้การลดขนาดได้ นอกจากนี้ UI Automator ยังช่วยในการเขียนการทดสอบ Macrobenchmark ด้วย
ฟีเจอร์หลักของแนวทางที่ทันสมัยมีดังนี้
uiAutomatorขอบเขตการทดสอบเฉพาะเพื่อโค้ดทดสอบที่สะอาดและสื่อความหมายมากขึ้น- เมธอดต่างๆ เช่น
onElement,onElementsและonElementOrNullสำหรับการค้นหาองค์ประกอบ UI ที่มีเพรดิเคตที่ชัดเจน - กลไกการรอในตัวสำหรับองค์ประกอบแบบมีเงื่อนไข
onElement*(timeoutMs: Long = 10000) - การจัดการสถานะของแอปอย่างชัดเจน เช่น
waitForStableและwaitForAppToBeVisible - การโต้ตอบโดยตรงกับโหนดหน้าต่างการช่วยเหลือพิเศษสำหรับสถานการณ์การทดสอบแบบหลายหน้าต่าง
- ความสามารถในการถ่ายภาพหน้าจอในตัวและ
ResultsReporterสำหรับการทดสอบภาพ และการแก้ไขข้อบกพร่อง
สร้างโปรเจ็กต์
หากต้องการเริ่มใช้ UI Automator API ที่ทันสมัย ให้อัปเดตไฟล์
build.gradle.kts ของโปรเจ็กต์ให้มีทรัพยากร Dependency ล่าสุด
Kotlin
dependencies {
...
androidTestImplementation("androidx.test.uiautomator:uiautomator:2.4.0-alpha05")
}
ดึงดูด
dependencies {
...
androidTestImplementation "androidx.test.uiautomator:uiautomator:2.4.0-alpha05"
}
แนวคิดหลักของ API
ส่วนต่อไปนี้จะอธิบายแนวคิดหลักของ Modern UI Automator API
ขอบเขตการทดสอบ uiAutomator
เข้าถึง UI Automator API ใหม่ทั้งหมดภายในบล็อก uiAutomator { ... }
ฟังก์ชันนี้จะสร้าง UiAutomatorTestScope ที่มีสภาพแวดล้อมที่กระชับ
และปลอดภัยสำหรับประเภทการดำเนินการทดสอบ
uiAutomator {
// All your UI Automator actions go here
startApp("com.example.targetapp")
onElement { textAsString() == "Hello, World!" }.click()
}
ค้นหาองค์ประกอบ UI
ใช้ UI Automator API กับ Predicate เพื่อค้นหาองค์ประกอบ UI โดยตัวระบุเหล่านี้ จะช่วยให้คุณกำหนดเงื่อนไขสำหรับพร็อพเพอร์ตี้ เช่น ข้อความ สถานะที่เลือกหรือโฟกัส และคำอธิบายเนื้อหาได้
onElement { predicate }: แสดงผลองค์ประกอบ UI แรกที่ตรงกับ เพรดิเคตภายในระยะหมดเวลาเริ่มต้น ฟังก์ชันจะยกเว้นหากไม่พบองค์ประกอบที่ตรงกัน// 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 }: รอจนกว่าองค์ประกอบ UI อย่างน้อย 1 รายการจะตรงกับ เพรดิเคตที่ระบุ จากนั้นจะแสดงผลรายการองค์ประกอบ UI ที่ตรงกันทั้งหมด// 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()
โต้ตอบกับองค์ประกอบ 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()
จัดการสถานะแอปและตัวสังเกตการณ์
จัดการวงจรของแอปและจัดการองค์ประกอบ UI ที่ไม่คาดคิดซึ่งอาจปรากฏขึ้นระหว่างการทดสอบ
การจัดการวงจรของแอป
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")
จัดการ UI ที่ไม่คาดคิด
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 เพื่อยืนยันว่า UI ของแอปถือว่าเสถียรก่อนที่จะโต้ตอบกับ UI
// 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 สำหรับ Macrobenchmark และโปรไฟล์พื้นฐาน
ใช้ UI Automator เพื่อทดสอบประสิทธิภาพด้วย Jetpack Macrobenchmark และเพื่อสร้างโปรไฟล์พื้นฐาน เนื่องจากเป็นวิธีที่เชื่อถือได้ในการ โต้ตอบกับแอปและวัดประสิทธิภาพจากมุมมองของผู้ใช้ปลายทาง
Macrobenchmark ใช้ UI Automator API เพื่อขับเคลื่อน UI และวัดการโต้ตอบ
เช่น ในการเปรียบเทียบประสิทธิภาพการเริ่มต้น คุณสามารถใช้ onElement เพื่อตรวจหาเวลาที่โหลดเนื้อหา UI เสร็จสมบูรณ์ ซึ่งจะช่วยให้คุณวัดเวลาจนกว่าจะแสดงผลเต็มหน้าจอ (TTFD) ได้ ในการเปรียบเทียบประสิทธิภาพการทำงานที่มีการกระตุก ระบบจะใช้ UI Automator API เพื่อเลื่อนรายการหรือเรียกใช้ภาพเคลื่อนไหวเพื่อวัดเวลาที่ใช้ในการแสดงผลเฟรม ฟังก์ชันอย่าง startActivity() หรือ
startIntent() มีประโยชน์ในการทำให้แอปอยู่ในสถานะที่ถูกต้องก่อนที่จะเริ่มการวัดผล
เมื่อสร้างโปรไฟล์พื้นฐาน คุณจะทำให้เส้นทางของผู้ใช้ที่สําคัญ (CUJ) ของแอปเป็นแบบอัตโนมัติเพื่อบันทึกคลาสและเมธอดที่ต้องมีการคอมไพล์ล่วงหน้า UI
Automator เป็นเครื่องมือที่เหมาะสำหรับการเขียนสคริปต์การทำงานอัตโนมัติเหล่านี้ การค้นหาองค์ประกอบตามตัวบ่งชี้ของ DSL สมัยใหม่และกลไกการรอในตัว (onElement) ช่วยให้การดำเนินการทดสอบมีความเสถียรและกำหนดได้มากขึ้นเมื่อเทียบกับวิธีอื่นๆ
ความเสถียรนี้ช่วยลดความไม่แน่นอนและทำให้มั่นใจได้ว่าโปรไฟล์พื้นฐานที่สร้างขึ้น จะแสดงเส้นทางโค้ดที่ดำเนินการในระหว่างโฟลว์ผู้ใช้ที่สำคัญที่สุด ได้อย่างถูกต้อง
ฟีเจอร์ขั้นสูง
ฟีเจอร์ต่อไปนี้มีประโยชน์สำหรับสถานการณ์การทดสอบที่ซับซ้อนมากขึ้น
โต้ตอบกับหลายหน้าต่าง
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 { 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 สำหรับบิตแมปช่วยให้การบันทึกรูปภาพที่แคปเจอร์ไปยังเส้นทางที่ระบุเป็นเรื่องง่าย
ใช้ 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 { viewIdResourceName == "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) |