Espresso는 두 가지 유형의 목록(어댑터 뷰와 recycler 뷰)에서 특정 항목으로 스크롤하거나 특정 항목에서 작업을 실행하는 메커니즘을 제공합니다.
목록, 특히 RecyclerView
또는 AdapterView
객체로 만들어진 목록을 처리할 때 소수의 하위 요소만 표시되고 스크롤할 때 재활용되므로 관심 있는 뷰가 화면에 표시되지 않을 수도 있습니다. 이 경우에는 기존 뷰가 필요하므로 scrollTo()
메서드를 사용할 수 없습니다.
어댑터 뷰 목록 항목과 상호작용
onView()
메서드를 사용하는 대신 onData()
로 검색을 시작하고 일치시키려는 뷰를 지원하는 데이터에 관한 매처를 제공합니다.
Espresso가 Adapter
객체에서 행을 찾고 표시 영역에 항목을 표시하는 모든 작업을 실행합니다.
맞춤 뷰 매처를 사용하여 데이터 일치
아래 활동에는 Map<String, Object>
객체에 있는 각 행의 데이터를 보유하는 SimpleAdapter
에서 지원하는 ListView
가 포함되어 있습니다.
각 맵에는 두 개의 항목이 있습니다. 문자열을 포함하는 "STR"
키(예: "item: x"
)와 콘텐츠의 길이를 나타내는 Integer
를 포함하는 "LEN"
키입니다. 예:
{"STR" : "item: 0", "LEN": 7}
'item: 50'이 있는 행을 클릭하기 위한 코드는 다음과 같습니다.
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());
Espresso는 필요에 따라 자동으로 목록을 스크롤합니다.
onData()
내부의 Matcher<Object>
를 분해해 보겠습니다. is(instanceOf(Map.class))
메서드는 Map
객체에서 지원하는 AdapterView
의 항목으로 검색 범위를 좁힙니다.
여기서는 이 쿼리 측면이 목록 뷰의 모든 행과 일치하지만 구체적으로 항목을 클릭하려고 하므로 다음을 사용하여 검색 범위를 더 좁힙니다.
Kotlin
hasEntry(equalTo("STR"), `is`("item: 50"))
Java
hasEntry(equalTo("STR"), is("item: 50"))
이 Matcher<String, Object>
는 "STR"
키와 "item: 50"
값이 있는 항목이 포함된 모든 맵과 일치합니다. 이를 찾는 코드는 길고 다른 위치에서 이 코드를 재사용하려고 하므로 이를 위한 맞춤 withItemContent
매처를 작성해 보겠습니다.
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); } };
Map
유형의 객체만 일치시키기 위해 BoundedMatcher
를 기본으로 사용합니다. matchesSafely()
메서드를 재정의하여 이전에 찾은 매처를 넣은 후 인수로 전달할 수 있는 Matcher<String>
와 일치시킵니다. 이렇게 하면 withItemContent(equalTo("foo"))
를 호출할 수 있습니다. 코드를 간단히 하기 위해 이미 equalTo()
를 호출하고 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)); }
이제 항목을 클릭하는 코드는 간단합니다.
Kotlin
onData(withItemContent("item: 50")).perform(click())
Java
onData(withItemContent("item: 50")).perform(click());
이 테스트의 전체 코드는 AdapterViewTest
클래스 내의 testClickOnItem50()
메서드와 GitHub의 이 맞춤 LongListMatchers
매처를 살펴보세요.
특정 하위 뷰 일치
위의 샘플은 ListView
의 전체 행 중간에서 클릭을 발생시킵니다.
하지만 행의 특정 하위 요소에 관해 작업하려면 어떻게 해야 하나요? 예를 들어 LongListActivity
행의 두 번째 열을 클릭하려고 합니다. 이 열은 첫 번째 열에 콘텐츠의 문자열 길이를 표시합니다.
DataInteraction
구현에 onChildView()
사양을 추가하기만 하면 됩니다.
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());
Recycler 뷰 목록 항목과 상호작용
RecyclerView
객체는 AdapterView
객체와 다르게 작동하므로 onData()
를 사용하여 객체와 상호작용할 수 없습니다.
Espresso를 사용하여 RecyclerView와 상호작용하려면 espresso-contrib
패키지를 사용하면 됩니다. 이 패키지에는 위치로 스크롤하거나 항목에 관한 작업을 실행하는 데 사용할 수 있는 RecyclerViewActions
컬렉션이 있습니다.
scrollTo()
- 일치하는 뷰가 있는 경우 그 뷰로 스크롤합니다.scrollToHolder()
- 일치하는 뷰 홀더가 있는 경우 이 뷰 홀더로 스크롤합니다.scrollToPosition()
- 특정 위치로 스크롤합니다.actionOnHolderItem()
- 일치하는 뷰 홀더에서 ViewAction을 실행합니다.actionOnItem()
- 일치하는 뷰에서 ViewAction을 실행합니다.actionOnItemAtPosition()
- 특정 위치의 뷰에서 ViewAction을 실행합니다.
다음 스니펫에는 RecyclerViewSample 샘플의 몇 가지 예가 포함되어 있습니다.
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())); }
추가 리소스
Android 테스트에서 Espresso 목록을 사용하는 방법에 관한 자세한 내용은 다음 리소스를 참고하세요.
샘플
- DataAdapterSample: 목록 및
AdapterView
객체에 관한 Espresso의onData()
진입점을 보여줍니다.