Kiến thức cơ bản về Espresso

Tài liệu này giải thích cách hoàn tất các thao tác kiểm thử tự động phổ biến bằng cách sử dụng API Espresso.

Espresso API khuyến khích tác giả kiểm thử suy nghĩ về những gì mà người dùng có thể thực hiện trong khi tương tác với ứng dụng – xác định vị trí các phần tử trên giao diện người dùng và tương tác với họ. Đồng thời, khung này cũng ngăn chặn quyền truy cập trực tiếp vào các hoạt động và lượt xem của ứng dụng do giữ các đối tượng này và trên chúng, ngoài luồng giao diện người dùng là một nguyên nhân chính gây ra tình trạng không ổn định. Do đó, bạn sẽ không thấy các phương thức như getView()getCurrentActivity() trong API Espresso. Bạn vẫn có thể vận hành an toàn trên các khung hiển thị bằng cách triển khai các lớp con của riêng bạn ViewActionViewAssertion.

Thành phần API

Các thành phần chính của Espresso bao gồm:

  • Espresso – Điểm truy cập đến hoạt động tương tác với khung hiển thị (thông qua onView()onData()). Đồng thời hiển thị các API không nhất thiết phải liên kết với bất kỳ khung hiển thị nào, chẳng hạn như dưới tên pressBack().
  • ViewMatchers – Một tập hợp các đối tượng triển khai phương thức Giao diện Matcher<? super View>. Bạn có thể truyền một hoặc nhiều mã này vào Phương thức onView() để xác định một khung hiển thị trong hệ phân cấp khung hiển thị hiện tại.
  • ViewActions – Một tập hợp các đối tượng ViewAction có thể được truyền đến phương thức ViewInteraction.perform(), chẳng hạn như click().
  • ViewAssertion – Một tập hợp các đối tượng ViewAssertion có thể được đã truyền phương thức ViewInteraction.check(). Trong mọi trường hợp, bạn sẽ sử dụng khớp với câu nhận định, sử dụng trình so khớp Khung hiển thị để xác nhận trạng thái của chế độ xem hiện được chọn.

Ví dụ:

Kotlin

// withId(R.id.my_view) is a ViewMatcher
// click() is a ViewAction
// matches(isDisplayed()) is a ViewAssertion
onView(withId(R.id.my_view))
    .perform(click())
    .check(matches(isDisplayed()))

Java

// withId(R.id.my_view) is a ViewMatcher
// click() is a ViewAction
// matches(isDisplayed()) is a ViewAssertion
onView(withId(R.id.my_view))
    .perform(click())
    .check(matches(isDisplayed()));

Tìm một chế độ xem

Trong hầu hết các trường hợp, phương thức onView() sẽ sử dụng trình so khớp hamcrest dự kiến sẽ khớp với một và chỉ một chế độ xem trong chế độ xem hiện tại thứ bậc. Trình so khớp là một công cụ hữu ích và quen thuộc với những ai đã từng sử dụng bằng Mockito hoặc JUnit. Nếu bạn chưa biết nhiều về công cụ so khớp hamcrest, chúng tôi bạn nên bắt đầu bằng việc xem nhanh thông tin này bản trình bày.

Thông thường, thành phần hiển thị mong muốn có một R.id duy nhất và một trình so khớp withId đơn giản sẽ thu hẹp tìm kiếm chế độ xem. Tuy nhiên, có nhiều trường hợp hợp pháp khi bạn không thể xác định R.id tại thời điểm phát triển kiểm thử. Ví dụ: chế độ xem cụ thể có thể không có R.id hoặc R.id không phải là duy nhất. Điều này có thể làm cho bình thường cho phép kiểm thử đo lường rất khó viết và phức tạp vì đây là cách thông thường để thì quyền truy cập vào khung hiển thị này (bằng findViewById()) không hoạt động. Do đó, bạn có thể cần truy cập vào các thành viên riêng tư của Hoạt động hoặc Mảnh chứa chế độ xem hoặc hãy tìm một vùng chứa có R.id đã biết rồi chuyển đến nội dung của vùng chứa đó chế độ xem cụ thể.

Espresso xử lý vấn đề này dễ dàng bằng cách cho phép bạn thu hẹp khung hiển thị bằng cách sử dụng đối tượng ViewMatcher hiện có hoặc đối tượng tuỳ chỉnh của riêng bạn.

Bạn có thể tìm một thành phần hiển thị theo R.id bằng cách gọi onView():

Kotlin

onView(withId(R.id.my_view))

Java

onView(withId(R.id.my_view));

Đôi khi, các giá trị R.id được dùng chung giữa nhiều chế độ xem. Khi điều này xảy ra, việc cố gắng sử dụng một R.id cụ thể sẽ mang lại cho bạn một ngoại lệ, chẳng hạn như AmbiguousViewMatcherException. Thông báo trường hợp ngoại lệ sẽ cung cấp cho bạn một văn bản bản trình bày của hệ phân cấp khung hiển thị hiện tại mà bạn có thể tìm kiếm và tìm thấy các khung hiển thị khớp với R.id không phải là duy nhất:

java.lang.RuntimeException:
androidx.test.espresso.AmbiguousViewMatcherException
This matcher matches multiple views in the hierarchy: (withId: is <123456789>)

...

+----->SomeView{id=123456789, res-name=plus_one_standard_ann_button,
visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true,
window-focus=true, is-focused=false, is-focusable=false, enabled=true,
selected=false, is-layout-requested=false, text=,
root-is-layout-requested=false, x=0.0, y=625.0, child-count=1}
****MATCHES****
|
+------>OtherView{id=123456789, res-name=plus_one_standard_ann_button,
visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true,
window-focus=true, is-focused=false, is-focusable=true, enabled=true,
selected=false, is-layout-requested=false, text=Hello!,
root-is-layout-requested=false, x=0.0, y=0.0, child-count=1}
****MATCHES****

Khi xem qua các thuộc tính khác nhau của khung hiển thị, bạn có thể thấy các thuộc tính có thể nhận dạng. Trong ví dụ trên, một trong các khung hiển thị có văn bản "Hello!". Bạn có thể sử dụng tuỳ chọn này để thu hẹp tìm kiếm của mình bằng cách sử dụng tổ hợp trình so khớp:

Kotlin

onView(allOf(withId(R.id.my_view), withText("Hello!")))

Java

onView(allOf(withId(R.id.my_view), withText("Hello!")));

Bạn cũng có thể chọn không đảo ngược bất kỳ trình so khớp nào:

Kotlin

onView(allOf(withId(R.id.my_view), not(withText("Unwanted"))))

Java

onView(allOf(withId(R.id.my_view), not(withText("Unwanted"))));

Xem ViewMatchers cho trình so khớp khung hiển thị do Espresso cung cấp.

Những yếu tố nên cân nhắc

  • Trong một ứng dụng hoạt động tốt, tất cả khung hiển thị mà người dùng có thể tương tác phải chứa văn bản mô tả hoặc có mô tả nội dung. Xem Tăng khả năng hỗ trợ tiếp cận của ứng dụng cho nhiều người dùng hơn chi tiết. Nếu bạn không thể thu hẹp nội dung tìm kiếm bằng withText() hoặc withContentDescription(), hãy xem xét việc này là một lỗi hỗ trợ tiếp cận.
  • Sử dụng trình so khớp ít mô tả nhất tìm được chế độ xem bạn đang tìm kiếm cho. Đừng chỉ định quá mức vì điều này sẽ buộc khung phải làm nhiều việc hơn là cần thiết. Ví dụ: nếu có thể nhận dạng chính xác một khung hiển thị bằng văn bản, thì bạn không cần chỉ định thành phần hiển thị cũng có thể gán từ TextView. Đối với nhiều R.id của khung hiển thị là đủ.
  • Nếu thành phần hiển thị đích nằm trong AdapterView (chẳng hạn như ListView), GridView hoặc Spinner – phương thức onView() có thể không hoạt động. Trong các trường hợp, bạn nên sử dụng onData().

Thực hiện thao tác trên một khung hiển thị

Sau khi tìm được trình so khớp phù hợp cho khung hiển thị mục tiêu, bạn có thể thực hiện các bản sao của ViewAction trên đó bằng phương thức perform.

Ví dụ: để nhấp vào chế độ xem, hãy làm như sau:

Kotlin

onView(...).perform(click())

Java

onView(...).perform(click());

Bạn có thể thực thi nhiều thao tác bằng một lệnh gọi thực hiện:

Kotlin

onView(...).perform(typeText("Hello"), click())

Java

onView(...).perform(typeText("Hello"), click());

Nếu thành phần hiển thị bạn đang xử lý nằm bên trong một ScrollView (dọc hoặc ngang), hãy xem xét các hành động trước đó yêu cầu khung hiển thị được hiển thị (chẳng hạn như click()typeText()) cùng với scrollTo(). Chiến dịch này giúp đảm bảo khung hiển thị sẽ hiện trước khi tiếp tục thao tác khác:

Kotlin

onView(...).perform(scrollTo(), click())

Java

onView(...).perform(scrollTo(), click());

Xem ViewActions cho các thao tác đối với thành phần hiển thị do Espresso cung cấp.

Kiểm tra xác nhận lượt xem

Bạn có thể áp dụng câu nhận định cho khung hiển thị hiện được chọn bằng check() . Đối tượng xác nhận được sử dụng nhiều nhất là lời khẳng định matches(). Chiến dịch này sử dụng một Đối tượng ViewMatcher để xác nhận trạng thái của khung hiển thị hiện được chọn.

Ví dụ: để kiểm tra nhằm đảm bảo rằng một thành phần hiển thị có văn bản "Hello!":

Kotlin

onView(...).check(matches(withText("Hello!")))

Java

onView(...).check(matches(withText("Hello!")));

Nếu bạn muốn xác nhận rằng "Hello!" là nội dung của thành phần hiển thị, thì những trường hợp sau đây được coi là phương pháp không hợp lệ:

Kotlin

// Don't use assertions like withText inside onView.
onView(allOf(withId(...), withText("Hello!"))).check(matches(isDisplayed()))

Java

// Don't use assertions like withText inside onView.
onView(allOf(withId(...), withText("Hello!"))).check(matches(isDisplayed()));

Mặt khác, nếu bạn muốn xác nhận rằng một thành phần hiển thị có văn bản "Hello!" là hiển thị (ví dụ: sau khi thay đổi cờ chế độ hiển thị), mã tốt.

Xem kiểm thử đơn giản về câu nhận định

Trong ví dụ này, SimpleActivity chứa ButtonTextView. Khi nút được nhấp vào, nội dung của TextView sẽ thay đổi thành "Hello Espresso!".

Sau đây là cách kiểm thử bằng Espresso:

Nhấp vào nút

Bước đầu tiên là tìm một thuộc tính giúp tìm thấy nút đó. Chiến lược phát hành đĩa đơn nút trong SimpleActivity có một R.id duy nhất, đúng như dự kiến.

Kotlin

onView(withId(R.id.button_simple))

Java

onView(withId(R.id.button_simple));

Bây giờ, để thực hiện nhấp chuột:

Kotlin

onView(withId(R.id.button_simple)).perform(click())

Java

onView(withId(R.id.button_simple)).perform(click());

Xác minh văn bản TextView

TextView kèm theo văn bản cần xác minh cũng có một R.id duy nhất:

Kotlin

onView(withId(R.id.text_simple))

Java

onView(withId(R.id.text_simple));

Bây giờ, hãy xác minh văn bản nội dung:

Kotlin

onView(withId(R.id.text_simple)).check(matches(withText("Hello Espresso!")))

Java

onView(withId(R.id.text_simple)).check(matches(withText("Hello Espresso!")));

Kiểm tra quá trình tải dữ liệu trong chế độ xem bộ chuyển đổi

AdapterView là một loại tiện ích đặc biệt có thể tự động tải dữ liệu từ một Bộ chuyển đổi. Ví dụ phổ biến nhất về AdapterViewListView. Như trái ngược với các tiện ích tĩnh như LinearLayout, đây chỉ là một tập hợp con của Bạn có thể tải AdapterView thành phần con vào hệ phân cấp khung hiển thị hiện tại. Một đơn giản Tìm kiếm onView() sẽ không tìm thấy các chế độ xem hiện chưa được tải.

Espresso xử lý việc này bằng cách cung cấp một điểm truy cập onData() riêng biệt trước tiên có thể tải mục bộ chuyển đổi được đề cập, đưa mục đó vào tiêu điểm trước khi thao tác trên nó hoặc bất kỳ phần tử con nào của nó.

Cảnh báo: Các cách triển khai tuỳ chỉnh của AdapterView có thể gặp sự cố với onData() nếu chúng phá vỡ hợp đồng thừa kế, đặc biệt là API getItem(). Trong những trường hợp như vậy, phương án hành động tốt nhất là tái cấu trúc mã xử lý ứng dụng. Nếu không thể làm như vậy, bạn có thể triển khai một khớp với AdapterViewProtocol tuỳ chỉnh. Để biết thêm thông tin, hãy tham khảo xem mục mặc định Lớp AdapterViewProtocols do Espresso cung cấp.

Kiểm thử đơn giản chế độ xem bộ chuyển đổi

Chương trình kiểm thử đơn giản này minh hoạ cách sử dụng onData(). SimpleActivity có chứa Spinner với một vài mặt hàng đại diện cho các loại cà phê. Khi một mục được chọn, có TextView thay đổi thành "One %s a day!", trong đó %s thể hiện mục đã chọn.

Mục tiêu của quy trình kiểm thử này là mở Spinner, chọn một mục cụ thể và xác minh rằng TextView chứa mục đó. Vì lớp Spinner dựa trên vàoAdapterView, bạn nên sử dụng onData() thay vì onView() cho khớp với mục.

Mở phần lựa chọn mặt hàng

Kotlin

onView(withId(R.id.spinner_simple)).perform(click())

Java

onView(withId(R.id.spinner_simple)).perform(click());

Hãy chọn một mục

Đối với phần lựa chọn mục, Spinner sẽ tạo một ListView với nội dung tương ứng. Chế độ xem này có thể rất dài và thành phần có thể không được đóng góp cho chế độ xem thứ bậc. Bằng cách sử dụng onData(), chúng ta buộc phần tử mong muốn vào khung hiển thị thứ bậc. Các mục trong Spinner là chuỗi nên chúng ta muốn so khớp một mục bằng Chuỗi "Americano":

Kotlin

onData(allOf(`is`(instanceOf(String::class.java)),
        `is`("Americano"))).perform(click())

Java

onData(allOf(is(instanceOf(String.class)), is("Americano"))).perform(click());

Xác minh rằng nội dung trong văn bản là chính xác

Kotlin

onView(withId(R.id.spinnertext_simple))
    .check(matches(withText(containsString("Americano"))))

Java

onView(withId(R.id.spinnertext_simple))
    .check(matches(withText(containsString("Americano"))));

Gỡ lỗi

Espresso cung cấp thông tin gỡ lỗi hữu ích khi kiểm thử không thành công:

Ghi nhật ký

Espresso ghi lại tất cả các thao tác xem vào logcat. Ví dụ:

ViewInteraction: Performing 'single click' action on view with text: Espresso

Hệ phân cấp chế độ xem

Espresso in hệ phân cấp khung hiển thị trong thông báo ngoại lệ khi onView() không thành công.

  • Nếu onView() không tìm thấy khung hiển thị đích, thì NoMatchingViewException sẽ là gửi. Bạn có thể kiểm tra hệ phân cấp khung hiển thị trong chuỗi ngoại lệ để phân tích lý do khiến trình so khớp không khớp với bất kỳ chế độ xem nào.
  • Nếu onView() tìm thấy nhiều khung hiển thị khớp với trình so khớp đã cho, thì Hệ thống sẽ gửi AmbiguousViewMatcherException. Hệ phân cấp khung hiển thị được in và tất cả các chế độ xem đã khớp sẽ được đánh dấu bằng nhãn MATCHES:
java.lang.RuntimeException:
androidx.test.espresso.AmbiguousViewMatcherException
This matcher matches multiple views in the hierarchy: (withId: is <123456789>)

...

+----->SomeView{id=123456789, res-name=plus_one_standard_ann_button,
visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true,
window-focus=true, is-focused=false, is-focusable=false, enabled=true,
selected=false, is-layout-requested=false, text=,
root-is-layout-requested=false, x=0.0, y=625.0, child-count=1}
****MATCHES****
|
+------>OtherView{id=123456789, res-name=plus_one_standard_ann_button,
visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true,
window-focus=true, is-focused=false, is-focusable=true, enabled=true,
selected=false, is-layout-requested=false, text=Hello!,
root-is-layout-requested=false, x=0.0, y=0.0, child-count=1}
****MATCHES****

Khi xử lý hệ phân cấp khung hiển thị phức tạp hoặc hành vi không mong muốn của tiện ích việc sử dụng Trình xem phân cấp trong Android Studio dành cho một lời giải thích.

Cảnh báo về chế độ xem bộ chuyển đổi

Espresso cảnh báo người dùng về sự hiện diện của tiện ích AdapterView. Khi một onView() sẽ gửi một tiện ích NoMatchingViewExceptionAdapterView có trong hệ phân cấp khung hiển thị, giải pháp phổ biến nhất là sử dụng onData(). Thông báo ngoại lệ sẽ bao gồm một cảnh báo kèm theo danh sách các chế độ xem bộ chuyển đổi. Bạn có thể dùng thông tin này để gọi onData() nhằm tải khung hiển thị mục tiêu.

Tài nguyên khác

Để biết thêm thông tin về cách sử dụng Espresso trong quy trình kiểm thử Android, hãy tham khảo các tài nguyên sau đây.

Mẫu