Viết kiểm thử tự động bằng UI Automator

Khung kiểm thử UI Automator cung cấp một bộ API để tạo các quy trình kiểm thử giao diện người dùng tương tác với ứng dụng người dùng và ứng dụng hệ thống.

Giới thiệu về hoạt động kiểm thử bằng UI Automator hiện đại

UI Automator 2.4 giới thiệu Ngôn ngữ dành riêng cho miền (DSL) thân thiện với Kotlin và được tinh giản, giúp đơn giản hoá việc viết các kiểm thử giao diện người dùng cho Android. Nền tảng API mới này tập trung vào việc tìm phần tử dựa trên vị từ và kiểm soát rõ ràng các trạng thái ứng dụng. Sử dụng công cụ này để tạo các kiểm thử tự động đáng tin cậy và dễ duy trì hơn.

UI Automator cho phép bạn kiểm thử một ứng dụng từ bên ngoài quy trình của ứng dụng. Điều này cho phép bạn kiểm thử các phiên bản phát hành đã được áp dụng quy trình rút gọn. UI Automator cũng giúp ích khi viết các kiểm thử macrobenchmark.

Các tính năng chính của phương pháp hiện đại bao gồm:

  • Một phạm vi kiểm thử uiAutomator chuyên biệt để có mã kiểm thử rõ ràng và biểu cảm hơn.
  • Các phương thức như onElement, onElementsonElementOrNull để tìm các phần tử trên giao diện người dùng bằng các vị từ rõ ràng.
  • Cơ chế chờ tích hợp cho các phần tử có điều kiện onElement*(timeoutMs: Long = 10000)
  • Quản lý trạng thái ứng dụng một cách tường minh, chẳng hạn như waitForStablewaitForAppToBeVisible.
  • Tương tác trực tiếp với các nút cửa sổ hỗ trợ tiếp cận cho các trường hợp kiểm thử nhiều cửa sổ.
  • Khả năng chụp ảnh màn hình tích hợp và ResultsReporter để kiểm thử và gỡ lỗi trực quan.

Thiết lập dự án

Để bắt đầu sử dụng các API UI Automator hiện đại, hãy cập nhật tệp build.gradle.kts của dự án để thêm phần phụ thuộc mới nhất:

Kotlin

dependencies {
  ...
  androidTestImplementation("androidx.test.uiautomator:uiautomator:2.4.0-alpha05")
}

Groovy

dependencies {
  ...
  androidTestImplementation "androidx.test.uiautomator:uiautomator:2.4.0-alpha05"
}

Các khái niệm cốt lõi về API

Các phần sau đây mô tả những khái niệm cốt lõi của API UI Automator hiện đại.

Phạm vi kiểm thử uiAutomator

Truy cập vào tất cả các API UI Automator mới trong khối uiAutomator { ... }. Hàm này tạo ra một UiAutomatorTestScope cung cấp một môi trường ngắn gọn và an toàn về kiểu cho các hoạt động kiểm thử của bạn.

uiAutomator {
  // All your UI Automator actions go here
  startApp("com.example.targetapp")
  onElement { textAsString() == "Hello, World!" }.click()
}

Tìm các phần tử trên giao diện người dùng

Sử dụng các API UI Automator với các vị từ để xác định vị trí của các phần tử trên giao diện người dùng. Các vị từ này cho phép bạn xác định các điều kiện cho các thuộc tính như văn bản, trạng thái đã chọn hoặc trạng thái được lấy tiêu điểm và nội dung mô tả.

  • onElement { predicate }: Trả về phần tử trên giao diện người dùng đầu tiên khớp với vị từ trong thời gian chờ mặc định. Hàm này sẽ gửi một ngoại lệ nếu không tìm thấy phần tử phù hợp.

    // 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 }: Tương tự như onElement, nhưng trả về null nếu hàm không tìm thấy phần tử nào khớp trong thời gian chờ. Hệ thống sẽ không gửi một trường hợp ngoại lệ. Hãy dùng phương thức này cho các phần tử không bắt buộc.

    val optionalButton = onElementOrNull { textAsString() == "Skip" }
    optionalButton?.click() // Click only if the button exists
    
  • onElements { predicate }: Chờ cho đến khi có ít nhất một phần tử trên giao diện người dùng khớp với vị từ đã cho, sau đó trả về danh sách tất cả các phần tử trên giao diện người dùng trùng khớp.

    // Get all items in a list Ui element
    val listItems = onElements { className == "android.widget.TextView" && isClickable }
    listItems.forEach { it.click() }
    

Dưới đây là một số mẹo sử dụng cuộc gọi onElement:

  • Chuỗi các lệnh gọi onElement cho các phần tử lồng nhau: Bạn có thể tạo chuỗi các lệnh gọi onElement để tìm các phần tử trong các phần tử khác, theo hệ thống phân cấp cha-con.

    // 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()
    
  • Chỉ định thời gian chờ cho các hàm onElement* bằng cách truyền một giá trị biểu thị mili giây.

    // 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()
    

Tương tác với các phần tử giao diện người dùng

Tương tác với các phần tử trên giao diện người dùng bằng cách mô phỏng các lượt nhấp hoặc đặt văn bản trong các trường có thể chỉnh sửa.

// 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()

Xử lý các trạng thái và trình theo dõi ứng dụng

Quản lý vòng đời của ứng dụng và xử lý các phần tử không mong muốn trên giao diện người dùng có thể xuất hiện trong quá trình kiểm thử.

Quản lý vòng đời của ứng dụng

Các API này cung cấp những cách thức để kiểm soát trạng thái của ứng dụng đang được kiểm thử:

// 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")

Xử lý giao diện người dùng không mong muốn

API watchFor cho phép bạn xác định trình xử lý cho các phần tử không mong muốn trên giao diện người dùng (chẳng hạn như hộp thoại cấp quyền) có thể xuất hiện trong quy trình kiểm thử. Thao tác này sử dụng cơ chế trình theo dõi nội bộ nhưng mang lại nhiều sự linh hoạt hơn.

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 là một ví dụ về ScopedWatcher<T>, trong đó T là đối tượng được truyền dưới dạng phạm vi đến khối trong watchFor. Bạn có thể tạo trình theo dõi tuỳ chỉnh dựa trên mẫu này.

Chờ khả năng hiển thị và độ ổn định của ứng dụng

Đôi khi, các kiểm thử cần đợi cho đến khi các phần tử xuất hiện hoặc ổn định. UI Automator cung cấp một số API để hỗ trợ việc này.

waitForAppToBeVisible("com.example.targetapp") đợi một phần tử trên giao diện người dùng có tên gói đã cho xuất hiện trên màn hình trong một khoảng thời gian chờ có thể tuỳ chỉnh.

// Wait for the app to be visible after launching it
startApp("com.example.targetapp")
waitForAppToBeVisible("com.example.targetapp")

Sử dụng API waitForStable() để xác minh rằng giao diện người dùng của ứng dụng được coi là ổn định trước khi tương tác với giao diện đó.

// 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()

Sử dụng UI Automator cho Macrobenchmark và Hồ sơ cơ sở

Sử dụng UI Automator để kiểm thử hiệu suất bằng Jetpack Macrobenchmark và để tạo Hồ sơ cơ sở, vì công cụ này cung cấp một cách đáng tin cậy để tương tác với ứng dụng của bạn và đo lường hiệu suất theo góc độ của người dùng cuối.

Macrobenchmark sử dụng các API UI Automator để điều khiển giao diện người dùng và đo lường các hoạt động tương tác. Ví dụ: trong các điểm chuẩn khởi động, bạn có thể sử dụng onElement để phát hiện thời điểm nội dung giao diện người dùng được tải đầy đủ, cho phép bạn đo lường Thời gian hiển thị khung hình đầu tiên với nội dung đầy đủ (TTFD). Trong các điểm chuẩn về hiện tượng giật, API UI Automator được dùng để cuộn danh sách hoặc chạy ảnh động nhằm đo thời gian hiển thị khung hình. Các hàm như startActivity() hoặc startIntent() rất hữu ích để đưa ứng dụng vào trạng thái chính xác trước khi bắt đầu đo lường.

Khi tạo Hồ sơ cơ sở, bạn sẽ tự động hoá các hành trình quan trọng của người dùng (CUJ) trong ứng dụng để ghi lại những lớp và phương thức cần được biên dịch trước. UI Automator là một công cụ lý tưởng để viết các tập lệnh tự động hoá này. Cơ chế tìm phần tử dựa trên vị từ và cơ chế chờ tích hợp của DSL hiện đại (onElement) giúp phiên chạy thử nghiệm trở nên mạnh mẽ và có kết quả thống nhất hơn so với các phương thức khác. Tính ổn định này giúp giảm tình trạng không ổn định và đảm bảo rằng Hồ sơ cơ sở được tạo phản ánh chính xác các đường dẫn mã được thực thi trong những quy trình quan trọng nhất của người dùng.

Các tính năng nâng cao

Các tính năng sau đây rất hữu ích cho những tình huống kiểm thử phức tạp hơn.

Tương tác với nhiều cửa sổ

API UI Automator cho phép bạn tương tác trực tiếp và kiểm tra các phần tử trên giao diện người dùng. Điều này đặc biệt hữu ích trong các trường hợp liên quan đến nhiều cửa sổ, chẳng hạn như chế độ Hình trong hình (PiP) hoặc bố cục chia đôi màn hình.

// 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()

Ảnh chụp màn hình và câu khẳng định trực quan

Chụp ảnh màn hình toàn bộ màn hình, các cửa sổ cụ thể hoặc các phần tử giao diện người dùng riêng lẻ ngay trong các bài kiểm thử. Điều này rất hữu ích cho việc kiểm thử hồi quy trực quan và gỡ lỗi.

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"))
}

Hàm tiện ích saveToFile cho Bitmap giúp đơn giản hoá việc lưu hình ảnh đã chụp vào một đường dẫn cụ thể.

Sử dụng ResultsReporter để gỡ lỗi

ResultsReporter giúp bạn liên kết các cấu phần phần mềm kiểm thử (chẳng hạn như ảnh chụp màn hình) trực tiếp với kết quả kiểm thử trong Android Studio để dễ dàng kiểm tra và gỡ lỗi.

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()
}

Di chuyển từ các phiên bản UI Automator cũ

Nếu bạn có các kiểm thử UI Automator hiện có được viết bằng các giao diện API cũ, hãy sử dụng bảng sau làm tài liệu tham khảo để di chuyển sang phương pháp hiện đại:

Loại thao tác Phương thức UI Automator cũ Phương thức UI Automator mới
Điểm truy cập UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) Gói logic kiểm thử trong phạm vi uiAutomator { ... }.
Tìm các phần tử trên giao diện người dùng device.findObject(By.res("com.example.app:id/my_button")) onElement { viewIdResourceName == "my\_button" }
Tìm các phần tử trên giao diện người dùng device.findObject(By.text("Click Me")) onElement { textAsString() == "Click Me" }
Đợi giao diện người dùng ở trạng thái rảnh device.waitForIdle() Ưu tiên cơ chế thời gian chờ tích hợp của onElement; nếu không, hãy dùng activeWindow().waitForStable()
Tìm phần tử con Cuộc gọi findObject được lồng theo cách thủ công onElement().onElement() xâu chuỗi
Xử lý hộp thoại quyền UiAutomator.registerWatcher() watchFor(PermissionDialog)