Espresso cung cấp các cơ chế cuộn đến hoặc thao tác trên một mục cụ thể các loại danh sách: thành phần hiển thị bộ chuyển đổi và thành phần hiển thị tái chế (recycler view).
Khi xử lý các danh sách, đặc biệt là những danh sách được tạo bằng RecyclerView
hoặc
AdapterView
, thì chế độ xem mà bạn quan tâm thậm chí có thể không xuất hiện
trên màn hình vì chỉ có một số ít phần tử con xuất hiện và
khi bạn cuộn. Không thể sử dụng phương thức scrollTo()
trong trường hợp này
vì cần có khung hiển thị hiện có.
Tương tác với các mục trong danh sách chế độ xem của bộ chuyển đổi
Thay vì sử dụng phương thức onView()
, hãy bắt đầu tìm kiếm của bạn bằng onData()
và
cung cấp trình so khớp dựa trên dữ liệu sao lưu chế độ xem mà bạn muốn so khớp.
Espresso sẽ thực hiện tất cả công việc tìm hàng trong đối tượng Adapter
và
làm cho mục hiển thị trong khung nhìn.
So khớp dữ liệu bằng trình so khớp khung hiển thị tuỳ chỉnh
Hoạt động bên dưới chứa ListView
, được SimpleAdapter
hỗ trợ
chứa dữ liệu cho từng hàng trong đối tượng Map<String, Object>
.
Mỗi tệp ánh xạ có hai mục nhập: khoá "STR"
chứa một Chuỗi, chẳng hạn như
"item: x"
và khoá "LEN"
chứa Integer
, đại diện cho
độ dài nội dung. Ví dụ:
{"STR" : "item: 0", "LEN": 7}
Mã cho một lượt nhấp vào hàng có "mặt hàng: 50" sẽ có dạng như sau:
Kotlin
onData(allOf(`is`(instanceOf(Map::class.java)), hasEntry(equalTo("STR"), `is`("item: 50")))).perform(click())
Java
onData(allOf(is(instanceOf(Map.class)), hasEntry(equalTo("STR"), is("item: 50")))) .perform(click());
Lưu ý rằng Espresso sẽ tự động cuộn qua danh sách khi cần.
Hãy tách Matcher<Object>
bên trong onData()
. Chiến lược phát hành đĩa đơn
Phương thức is(instanceOf(Map.class))
thu hẹp tìm kiếm vào bất kỳ mục nào trong
AdapterView
được đối tượng Map
hỗ trợ.
Trong trường hợp này, khía cạnh này của truy vấn phù hợp với mọi dòng của chế độ xem danh sách, nhưng chúng ta muốn nhấp vào một mục cụ thể, nên chúng tôi thu hẹp tìm kiếm thêm bằng:
Kotlin
hasEntry(equalTo("STR"), `is`("item: 50"))
Java
hasEntry(equalTo("STR"), is("item: 50"))
Matcher<String, Object>
này sẽ khớp với bất kỳ Bản đồ nào có chứa mục nhập với
khoá "STR"
và giá trị "item: 50"
. Vì mã để tra cứu này là
và chúng tôi muốn sử dụng lại địa chỉ đó ở các vị trí khác, hãy viết một đường dẫn tuỳ chỉnh
withItemContent
trình so khớp cho điều đó:
Kotlin
return object : BoundedMatcher<Object, Map>(Map::class.java) { override fun matchesSafely(map: Map): Boolean { return hasEntry(equalTo("STR"), itemTextMatcher).matches(map) } override fun describeTo(description: Description) { description.appendText("with item content: ") itemTextMatcher.describeTo(description) } }
Java
return new BoundedMatcher<Object, Map>(Map.class) { @Override public boolean matchesSafely(Map map) { return hasEntry(equalTo("STR"), itemTextMatcher).matches(map); } @Override public void describeTo(Description description) { description.appendText("with item content: "); itemTextMatcher.describeTo(description); } };
Bạn dùng BoundedMatcher
làm cơ sở vì để chỉ so khớp các đối tượng thuộc kiểu
Map
. Ghi đè phương thức matchesSafely()
, đưa vào trình so khớp tìm thấy
trước đó rồi so khớp mã đó với một Matcher<String>
mà bạn có thể chuyển dưới dạng
đối số. Thao tác này cho phép bạn gọi withItemContent(equalTo("foo"))
. Để viết mã
Để ngắn gọn, bạn có thể tạo một trình so khớp khác đã gọi equalTo()
và
chấp nhận đối tượng String
:
Kotlin
fun withItemContent(expectedText: String): Matcher<Object> { checkNotNull(expectedText) return withItemContent(equalTo(expectedText)) }
Java
public static Matcher<Object> withItemContent(String expectedText) { checkNotNull(expectedText); return withItemContent(equalTo(expectedText)); }
Bây giờ, mã để nhấp vào mục đó rất đơn giản:
Kotlin
onData(withItemContent("item: 50")).perform(click())
Java
onData(withItemContent("item: 50")).perform(click());
Để xem mã đầy đủ của quy trình kiểm thử này, hãy tìm hiểu phương thức testClickOnItem50()
trong
AdapterViewTest
lớp và
LongListMatchers
tuỳ chỉnh này
trình so khớp trên GitHub.
Khớp với một chế độ xem cụ thể của trẻ
Mẫu ở trên đưa ra một lượt nhấp ở giữa toàn bộ hàng của ListView
.
Nhưng nếu chúng ta muốn thao tác trên một thành phần con cụ thể của hàng thì sao? Ví dụ: chúng tôi
muốn nhấp vào cột thứ hai trong hàng LongListActivity
,
hiển thị String.length của nội dung trong cột đầu tiên:
Bạn chỉ cần thêm thông số kỹ thuật onChildView()
vào quá trình triển khai
DataInteraction
:
Kotlin
onData(withItemContent("item: 60")) .onChildView(withId(R.id.item_size)) .perform(click())
Java
onData(withItemContent("item: 60")) .onChildView(withId(R.id.item_size)) .perform(click());
Tương tác với các mục trong danh sách khung hiển thị tái chế
Các đối tượng RecyclerView
hoạt động khác với đối tượng AdapterView
, vì vậy
Bạn không thể sử dụng onData()
để tương tác với các tài khoản đó.
Để tương tác với RecyclerView bằng Espresso, bạn có thể sử dụng
Gói espresso-contrib
, trong đó có một tập hợp
RecyclerViewActions
có thể dùng để cuộn đến vị trí hoặc thực hiện thao tác trên các mục:
scrollTo()
– Cuộn đến Chế độ xem phù hợp, nếu tồn tại.scrollToHolder()
– Cuộn đến Trình sở hữu khung hiển thị trùng khớp, nếu có.scrollToPosition()
– Cuộn đến một vị trí cụ thể.actionOnHolderItem()
– Thực hiện hành động thành phần hiển thị trên một thành phần lưu giữ thành phần hiển thị trùng khớp.actionOnItem()
– Thực hiện hành động thành phần hiển thị trên một thành phần hiển thị trùng khớp.actionOnItemAtPosition()
– Thực hiện ViewAction trên một thành phần hiển thị tại một vị trí cụ thể.
Các đoạn mã sau đây cho thấy một số ví dụ từ RecyclerViewMẫu mẫu:
Kotlin
@Test(expected = PerformException::class) fun itemWithText_doesNotExist() { // Attempt to scroll to an item that contains the special text. onView(ViewMatchers.withId(R.id.recyclerView)) .perform( // scrollTo will fail the test if no item matches. RecyclerViewActions.scrollTo( hasDescendant(withText("not in the list")) ) ) }
Java
@Test(expected = PerformException.class) public void itemWithText_doesNotExist() { // Attempt to scroll to an item that contains the special text. onView(ViewMatchers.withId(R.id.recyclerView)) // scrollTo will fail the test if no item matches. .perform(RecyclerViewActions.scrollTo( hasDescendant(withText("not in the list")) )); }
Kotlin
@Test fun scrollToItemBelowFold_checkItsText() { // First, scroll to the position that needs to be matched and click on it. onView(ViewMatchers.withId(R.id.recyclerView)) .perform( RecyclerViewActions.actionOnItemAtPosition( ITEM_BELOW_THE_FOLD, click() ) ) // Match the text in an item below the fold and check that it's displayed. val itemElementText = "${activityRule.activity.resources .getString(R.string.item_element_text)} ${ITEM_BELOW_THE_FOLD.toString()}" onView(withText(itemElementText)).check(matches(isDisplayed())) }
Java
@Test public void scrollToItemBelowFold_checkItsText() { // First, scroll to the position that needs to be matched and click on it. onView(ViewMatchers.withId(R.id.recyclerView)) .perform(RecyclerViewActions.actionOnItemAtPosition(ITEM_BELOW_THE_FOLD, click())); // Match the text in an item below the fold and check that it's displayed. String itemElementText = activityRule.getActivity().getResources() .getString(R.string.item_element_text) + String.valueOf(ITEM_BELOW_THE_FOLD); onView(withText(itemElementText)).check(matches(isDisplayed())); }
Kotlin
@Test fun itemInMiddleOfList_hasSpecialText() { // First, scroll to the view holder using the isInTheMiddle() matcher. onView(ViewMatchers.withId(R.id.recyclerView)) .perform(RecyclerViewActions.scrollToHolder(isInTheMiddle())) // Check that the item has the special text. val middleElementText = activityRule.activity.resources .getString(R.string.middle) onView(withText(middleElementText)).check(matches(isDisplayed())) }
Java
@Test public void itemInMiddleOfList_hasSpecialText() { // First, scroll to the view holder using the isInTheMiddle() matcher. onView(ViewMatchers.withId(R.id.recyclerView)) .perform(RecyclerViewActions.scrollToHolder(isInTheMiddle())); // Check that the item has the special text. String middleElementText = activityRule.getActivity().getResources() .getString(R.string.middle); onView(withText(middleElementText)).check(matches(isDisplayed())); }
Tài nguyên khác
Để biết thêm thông tin về cách sử dụng danh sách Espresso trong quy trình kiểm thử Android, hãy tham khảo các tài nguyên sau đây.
Mẫu
- DataAdapterSample:
Trình bày điểm truy cập
onData()
cho Espresso, cho danh sách vàAdapterView
.