Bản chất không đồng bộ của các ứng dụng và khung di động thường khiến bạn gặp khó khăn khi viết mã kiểm thử đáng tin cậy và có thể lặp lại. Khi một sự kiện người dùng được chèn, khung kiểm thử phải đợi ứng dụng hoàn tất việc phản ứng với sự kiện đó, có thể là thay đổi một số văn bản trên màn hình cho đến việc tạo lại hoàn toàn một hoạt động. Khi một kiểm thử không có hành vi xác định, thì kiểm thử đó là không ổn định.
Các khung hiện đại như Compose hoặc Espresso được thiết kế để kiểm thử, vì vậy, có một đảm bảo chắc chắn rằng giao diện người dùng sẽ ở trạng thái rảnh trước hành động kiểm thử hoặc câu nhận định tiếp theo. Đây là quá trình đồng bộ hoá.
Đồng bộ hoá kiểm thử
Vẫn có thể xảy ra vấn đề khi bạn chạy các hoạt động không đồng bộ hoặc ở chế độ nền mà kiểm thử không biết, chẳng hạn như tải dữ liệu từ cơ sở dữ liệu hoặc hiển thị ảnh động vô hạn.
Để tăng độ tin cậy của bộ kiểm thử, bạn có thể cài đặt một cách để theo dõi các thao tác trong nền, chẳng hạn như Tài nguyên không hoạt động của Espresso. Ngoài ra, bạn có thể thay thế các mô-đun cho các phiên bản kiểm thử mà bạn có thể truy vấn để biết trạng thái rảnh hoặc các mô-đun cải thiện khả năng đồng bộ hoá, chẳng hạn như TestDispatcher cho coroutine hoặc RxIdler cho RxJava.
Cách cải thiện độ ổn định
Các chương trình kiểm thử lớn có thể phát hiện nhiều lỗi hồi quy cùng một lúc vì chúng kiểm thử nhiều thành phần của một ứng dụng. Các chương trình kiểm thử này thường chạy trên trình mô phỏng hoặc thiết bị, nghĩa là chúng có độ chân thực cao. Mặc dù các kiểm thử toàn diện lớn cung cấp phạm vi kiểm thử toàn diện, nhưng chúng dễ gặp lỗi hơn.
Sau đây là các biện pháp chính mà bạn có thể thực hiện để giảm tình trạng không ổn định:
- Định cấu hình thiết bị đúng cách
- Ngăn chặn sự cố đồng bộ hoá
- Triển khai thử lại
Để tạo các quy trình kiểm thử lớn bằng Compose hoặc Espresso, bạn thường bắt đầu một trong các hoạt động và điều hướng như người dùng, xác minh rằng giao diện người dùng hoạt động chính xác bằng cách sử dụng các câu nhận định hoặc kiểm thử ảnh chụp màn hình.
Các khung khác, chẳng hạn như Trình tự động hoá giao diện người dùng, cho phép phạm vi lớn hơn vì bạn có thể tương tác với giao diện người dùng hệ thống và các ứng dụng khác. Tuy nhiên, các bài kiểm thử Trình tự động hoá giao diện người dùng có thể yêu cầu đồng bộ hoá thủ công nhiều hơn nên chúng có xu hướng kém tin cậy hơn.
Định cấu hình thiết bị
Trước tiên, để cải thiện độ tin cậy của chương trình kiểm thử, bạn cần đảm bảo rằng hệ điều hành của thiết bị không đột ngột làm gián đoạn quá trình kiểm thử. Ví dụ: khi hộp thoại cập nhật hệ thống xuất hiện trên các ứng dụng khác hoặc khi không đủ dung lượng trên ổ đĩa.
Các nhà cung cấp trang trại thiết bị định cấu hình thiết bị và trình mô phỏng của họ, vì vậy, thông thường bạn không phải làm gì cả. Tuy nhiên, các lớp này có thể có các lệnh cấu hình riêng cho các trường hợp đặc biệt.
Thiết bị do Gradle quản lý
Nếu tự quản lý trình mô phỏng, bạn có thể sử dụng thiết bị do Gradle quản lý để xác định thiết bị sẽ dùng để chạy kiểm thử:
android {
testOptions {
managedDevices {
localDevices {
create("pixel2api30") {
// Use device profiles you typically see in Android Studio.
device = "Pixel 2"
// Use only API levels 27 and higher.
apiLevel = 30
// To include Google services, use "google".
systemImageSource = "aosp"
}
}
}
}
}
Với cấu hình này, lệnh sau sẽ tạo hình ảnh trình mô phỏng, khởi động một thực thể, chạy các chương trình kiểm thử và tắt thực thể đó.
./gradlew pixel2api30DebugAndroidTest
Thiết bị do Gradle quản lý có chứa các cơ chế để thử lại trong trường hợp thiết bị bị ngắt kết nối và các điểm cải tiến khác.
Ngăn chặn các sự cố đồng bộ hoá
Các thành phần thực hiện các thao tác ở chế độ nền hoặc không đồng bộ có thể dẫn đến lỗi kiểm thử vì câu lệnh kiểm thử đã được thực thi trước khi giao diện người dùng sẵn sàng cho thao tác đó. Khi phạm vi kiểm thử mở rộng, khả năng kiểm thử không ổn định sẽ tăng lên. Các vấn đề đồng bộ hoá này là nguyên nhân chính gây ra sự không ổn định vì khung kiểm thử cần suy luận xem một hoạt động đã hoàn tất việc tải hay chưa hoặc liệu hoạt động đó có cần chờ lâu hơn hay không.
Giải pháp
Bạn có thể sử dụng tài nguyên không đồng bộ của Espresso để cho biết thời điểm một ứng dụng bận, nhưng khó theo dõi mọi hoạt động không đồng bộ, đặc biệt là trong các kiểm thử toàn diện rất lớn. Ngoài ra, bạn có thể khó cài đặt tài nguyên ở trạng thái rảnh mà không làm hỏng mã đang được kiểm thử.
Thay vì ước tính xem một hoạt động có bận hay không, bạn có thể yêu cầu các bài kiểm thử chờ cho đến khi đáp ứng các điều kiện cụ thể. Ví dụ: bạn có thể đợi cho đến khi một văn bản hoặc thành phần cụ thể xuất hiện trong giao diện người dùng.
Compose có một bộ sưu tập các API kiểm thử trong ComposeTestRule
để chờ các trình so khớp khác nhau:
fun waitUntilAtLeastOneExists(matcher: SemanticsMatcher, timeout: Long = 1000L)
fun waitUntilDoesNotExist(matcher: SemanticsMatcher, timeout: Long = 1000L)
fun waitUntilExactlyOneExists(matcher: SemanticsMatcher, timeout: Long = 1000L)
fun waitUntilNodeCount(matcher: SemanticsMatcher, count: Int, timeout: Long = 1000L)
Và một API chung nhận bất kỳ hàm nào trả về một boolean:
fun waitUntil(timeoutMillis: Long, condition: () -> Boolean): Unit
Ví dụ về cách sử dụng:
composeTestRule.waitUntilExactlyOneExists(hasText("Continue")</code>)</p></td>
Cơ chế thử lại
Bạn nên khắc phục các bài kiểm thử không ổn định, nhưng đôi khi các điều kiện khiến các bài kiểm thử không đạt lại khó xảy ra đến mức khó tái tạo. Mặc dù bạn phải luôn theo dõi và khắc phục các kiểm thử không ổn định, nhưng cơ chế thử lại có thể giúp duy trì năng suất của nhà phát triển bằng cách chạy kiểm thử một số lần cho đến khi kiểm thử thành công.
Bạn cần thử lại ở nhiều cấp độ để ngăn chặn các vấn đề, chẳng hạn như:
- Đã hết thời gian chờ kết nối với thiết bị hoặc mất kết nối
- Không kiểm thử được một lần
Việc cài đặt hoặc định cấu hình lượt thử lại phụ thuộc vào khung kiểm thử và cơ sở hạ tầng của bạn, nhưng các cơ chế thông thường bao gồm:
- Quy tắc JUnit thử lại bất kỳ kiểm thử nào một số lần
- Một hành động hoặc bước thử lại trong quy trình làm việc CI
- Hệ thống để khởi động lại trình mô phỏng khi trình mô phỏng không phản hồi, chẳng hạn như các thiết bị do Gradle quản lý.