Espresso là framework kiểm thử UI tự động cho các ứng dụng Android, được phát triển bởi nhóm Google và là một phần của AndroidX Test. Không giống như kiểm thử công cụ kiểm tra các thành phần riêng lẻ, Espresso tương tác với UI thực: nhấp nút, nhập văn bản, kiểm tra hiển thị phần tử. Theo Google Android Developers, Espresso cung cấp đồng bộ tự động với luồng UI, loại bỏ nhu cầu sử dụng Thread.sleep() thủ công.
Những điểm chính
Espresso là thư viện viết kiểm thử UI tự động cho Android, một phần của Google AndroidX Test. Nó cung cấp API để tìm phần tử View trên màn hình, thực hiện hành động trên chúng (nhấp, nhập, vuốt) và kiểm tra trạng thái của chúng (hiển thị, chứa văn bản, được kích hoạt).
Tính năng chính của Espresso là đồng bộ tự động với luồng chính của ứng dụng. Framework chờ tất cả các tác vụ bất đồng bộ (coroutine, AsyncTask, Handler) hoàn thành trước khi thực hiện kiểm tra tiếp theo. Điều này loại bỏ các kiểm thử không ổn định do điều kiện cạnh tranh và làm cho kiểm thử UI ổn định và đáng tin cậy — không kiểm thử nào chứa Thread.sleep() hoặc vòng lặp chờ.
Espresso tuân theo nguyên tắc Chó Ba Chân — một kiểm thử gồm ba bước: tìm phần tử (ViewMatcher), thực hiện hành động (ViewAction), kiểm tra kết quả (ViewAssertion). Cả ba bước được viết trong một chuỗi gọi: onView().perform().check(). Khái niệm này làm cho kiểm thử dễ dự đoán và dễ đọc — mỗi kiểm thử mô tả rõ ràng nó tìm gì, làm gì và kiểm tra gì.
Kiến trúc của Espresso dựa trên ba thành phần: Espresso (điểm vào — phương thức tĩnh onView và onData), ViewMatchers (tìm kiếm phần tử), ViewActions (hành động) và ViewAssertions (kiểm tra). Bên trong, framework sử dụng Idling Resource để đồng bộ với luồng UI.
Kiểm thử đơn giản nhất tìm nút theo ID, thực hiện nhấp và kiểm tra văn bản “Hoàn tất” xuất hiện. Tất cả thao tác đều đồng bộ từ góc nhìn kiểm thử — Espresso đảm bảo luồng UI đã xử lý xong sự kiện trước khi kiểm thử tiếp tục. Điều này đạt được nhờ cơ chế chờ tích hợp: onView chặn thực thi kiểm thử cho đến khi UI ở trạng thái rảnh.
@Test
fun buttonClick_showsSuccessText() {
// Tìm nút theo ID và nhấp
onView(withId(R.id.button_submit))
.perform(click())
// Kiểm tra văn bản “Hoàn tất” được hiển thị
onView(withText("Hoàn tất"))
.check(matches(isDisplayed()))
}
Để khởi chạy kiểm thử Espresso, ActivityScenario (AndroidX Test) được sử dụng, tạo một Activity ở trạng thái cụ thể — đang chạy, tạm dừng hoặc bị hủy. ActivityScenario cho phép kiểm tra vòng đời Activity ngoài kiểm thử UI thuần túy. Ví dụ, bạn có thể kiểm tra dữ liệu được bảo toàn khi xoay màn hình (tái tạo Activity) và được khôi phục sau khi hủy.
ViewMatchers là tập hợp các phương thức từ lớp Espresso.onView cho phép tìm Views trên màn hình theo nhiều tiêu chí: ID tài nguyên (R.id), văn bản, gợi ý, phần tử cha và hệ phân cấp. Nếu một matcher không cho kết quả duy nhất, các matcher có thể được kết hợp bằng allOf().
| Matcher | Mục đích |
|---|---|
| withId(R.id.name) | Tìm theo ID tài nguyên |
| withText(“văn bản”) | Tìm theo văn bản hiển thị |
| withHint(“gợi ý”) | Tìm theo thuộc tính gợi ý của EditText |
| isDisplayed() | Kiểm tra phần tử hiển thị trên màn hình |
| hasSibling(matcher) | Tìm theo phần tử anh em |
| allOf(m1, m2) | Kết hợp nhiều matcher |
Nếu có nhiều phần tử giống nhau trên màn hình (ví dụ, hai TextView với văn bản khác nhau), có thể kết hợp matcher bằng allOf: onView(allOf(withId(R.id.title), withText(“Xin chào”))). Điều này đảm bảo chọn một phần tử duy nhất. Toán tử nghịch đảo — not() — loại trừ phần tử khỏi tìm kiếm, và hasSibling() tìm phần tử bên cạnh phần tử đã biết.
ViewActions là các hành động Espresso thực hiện trên View tìm được: click(), typeText(), clearText(), scrollTo(), swipeLeft() và các hành động khác. Các hành động được truyền vào phương thức perform(), có thể chấp nhận nhiều hành động liên tiếp.
Phương thức perform() chấp nhận vararg ViewAction, cho phép thực hiện chuỗi hành động trên một phần tử: xóa trường, nhập văn bản mới, đóng bàn phím và nhấp nút. Tất cả hành động thực hiện theo thứ tự liệt kê, và Espresso đảm bảo hành động trước hoàn thành trước khi hành động tiếp theo bắt đầu.
// Nhập văn bản vào EditText và nhấp nút
onView(withId(R.id.edit_email))
.perform(
clearText(),
typeText("user@example.com"),
closeSoftKeyboard()
)
onView(withId(R.id.button_login))
.perform(click())
Đối với phần tử bên trong AdapterView (ListView, RecyclerView), phương thức onData() được sử dụng thay vì onView. Nó làm việc với dữ liệu adapter thay vì Views — tìm phần tử theo nội dung model và trả về View tương ứng cho các hành động tiếp theo. onData sử dụng matcher hamcrest để xác định vị trí phần tử theo trường dữ liệu model.
ViewAssertions kiểm tra View ở trạng thái cụ thể. Phương thức cơ bản — matches(matcher) — kiểm tra phần tử khớp với matcher đã cho. Ngoài ra, Espresso cung cấp doesNotExist() (phần tử không tồn tại) và selectedDescendantsMatch() (kiểm tra phần tử lồng nhau).
Các kiểm tra phổ biến nhất trong kiểm thử UI: phần tử hiển thị (isDisplayed), phần tử chứa văn bản cụ thể (withText), phần tử được kích hoạt (isEnabled), phần tử không được chọn (isNotChecked). Mỗi kiểm tra ném ngoại lệ chi tiết khi thất bại — bao gồm hệ phân cấp View trên màn hình. Điều này đơn giản hóa việc gỡ lỗi: thông báo lỗi hiển thị phần tử nào thực sự có trên màn hình tại thời điểm kiểm tra.
Nếu kiểm tra tiêu chuẩn không đủ, có thể tạo kiểm tra tùy chỉnh qua giao diện ViewAssertion. Một assertion tùy chỉnh nhận View và có thể kiểm tra trạng thái của nó theo chương trình — ví dụ, màu văn bản, đệm hoặc trạng thái của thành phần tùy chỉnh không được hiển thị qua matcher tiêu chuẩn.
// Kiểm tra: TextView hiển thị và chứa văn bản
onView(withId(R.id.text_welcome))
.check(matches(isDisplayed()))
.check(matches(withText("Chào mừng")))
// Kiểm tra: phần tử KHÔNG hiển thị
onView(withId(R.id.progress_bar))
.check(doesNotExist())
Idling Resource là cơ chế của Espresso để đồng bộ kiểm thử với các thao tác bất đồng bộ. Theo mặc định, Espresso chờ Handler, AsyncTask và coroutine (qua coroutinesIdlingResource). Nếu ứng dụng thực hiện công việc nền qua luồng tùy chỉnh hoặc dịch vụ callback, cần đăng ký Idling Resource tùy chỉnh.
Từ AndroidX Test 1.4.0, Espresso hỗ trợ coroutine qua CoroutinesIdlingResource. Kiểm thử tự động chờ tất cả coroutine đã khởi chạy hoàn thành trước khi thực hiện kiểm tra UI. Đối với kịch bản phức tạp hơn, CountingIdlingResource được sử dụng — bộ đếm tăng khi bắt đầu tác vụ và giảm khi hoàn thành.
// Đăng ký IdlingResource cho OkHttp
class OkHttpIdlingResource(
private val client: OkHttpClient
) : IdlingResource {
private var isIdle = true
private var watcher: IdlingResource.ResourceCallback? = null
override fun getName() = "OkHttp"
override fun isIdleNow() = isIdle
override fun registerIdleTransitionCallback(
callback: IdlingResource.ResourceCallback
) {
watcher = callback
}
}
Kết nối Espresso vào dự án Android được thực hiện bằng cách thêm phụ thuộc vào build.gradle cấp module. Espresso là một phần của AndroidX Test, vì vậy chỉ cần chỉ định phụ thuộc cho lõi Espresso, tiện ích mở rộng và tích hợp JUnit. Kiểm thử được đặt trong thư mục src/androidTest và chạy trên thiết bị vật lý hoặc giả lập qua AndroidJUnitRunner.
Tập phụ thuộc tối thiểu bao gồm espresso-core (lõi), espresso-contrib (matcher bổ sung cho RecyclerView, Drawer, Picker) và runner (trình chạy kiểm thử AndroidX). Tất cả kiểm thử chạy trên giả lập hoặc thiết bị vật lý qua Android Test Orchestrator.
// build.gradle.kts (phụ thuộc androidTest)
android {
defaultConfig {
testInstrumentationRunner =
"androidx.test.runner.AndroidJUnitRunner"
}
}
dependencies {
androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1")
androidTestImplementation("androidx.test.espresso:espresso-contrib:3.6.1")
androidTestImplementation("androidx.test:runner:1.6.1")
androidTestImplementation("androidx.test:rules:1.6.1")
}
Kiểm thử Espresso có thể chạy qua Google Android Test Orchestrator, cách ly mỗi kiểm thử trong một tiến trình riêng và xóa trạng thái giữa các lần chạy. Điều này loại bỏ kiểm thử không ổn định do dữ liệu còn sót từ kiểm thử trước và cải thiện độ ổn định trên máy chủ CI. Để thực thi song song, sharding được sử dụng — phân phối kiểm thử giữa nhiều giả lập.
Câu hỏi thường gặp
Espresso hoạt động bên trong tiến trình ứng dụng và sử dụng đồng bộ tự động với luồng UI. UI Automator hoạt động ở cấp hệ thống, có thể tương tác với ứng dụng khác, nhưng yêu cầu quản lý chờ thủ công.
Đây là phép ẩn dụ từ bài thuyết trình của Google: kiểm thử Espresso đứng trên ba trụ cột — ViewMatcher (tìm), ViewAction(hành động) và ViewAssertion(kiểm tra). Bỏ bất kỳ cột nào, kiểm thử mất ổn định, như con chó ba chân.
Đối với RecyclerView, thư viện espresso-contrib được sử dụng với các phương thức như onView(withId(R.id.recycler)).perform(actionOnItemAtPosition(0, click())). Thay thế là onData() cho AdapterView hoặc ViewAction tùy chỉnh để tìm phần tử theo văn bản trong RecyclerView. Ngoài ra, RecyclerViewActions từ espresso-contrib có thể được dùng để cuộn đến phần tử và thực hiện hành động trên đó.
Kiểm thử không ổn định là kiểm thử đôi khi thất bại mà không thay đổi mã, do điều kiện cạnh tranh hoặc bất đồng bộ. Espresso giải quyết vấn đề này bằng Idling Resource — chờ tất cả tác vụ nền hoàn thành trước khi thực hiện kiểm tra.
Bản thân Espresso không được thiết kế cho kiểm thử ảnh chụp màn hình, nhưng có thể kết hợp với các thư viện như Shot hoặc Paparazzi. Espresso chuẩn bị UI ở trạng thái mong muốn, và thư viện so sánh chụp ảnh màn hình và so sánh với tham chiếu. Cách tiếp cận này gọi là kiểm thử hồi quy trực quan và giúp tìm các thay đổi không mong đợi trong giao diện.
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm