Espresso リスト

Espresso には、2 つのユーザーを対象に、特定のアイテムまでスクロールする、または特定のアイテムを操作するメカニズムが備わっています。 アダプター ビューとリサイクラー ビューがあります。

リスト、特に RecyclerViewAdapterView オブジェクトを使用すると、目的のビューがまだ存在しない可能性があります。 表示されるのは少数の子要素のみであるため、 スクロールするとリサイクルされますこの場合、scrollTo() メソッドは使用できません。 既存のビューが必要であるためです

アダプター ビューのリスト項目の操作

onView() メソッドを使用する代わりに、onData() で検索を開始します。 マッチングするビューのベースとなるデータに対するマッチャーを提供します。 Espresso は、Adapter オブジェクト内の行を見つける作業をすべて行い、 ビューポートにアイテムを表示します

カスタムビュー マッチャーを使用したデータのマッチング

以下のアクティビティには、SimpleAdapter を基盤とする ListView が含まれています。 これは、Map<String, Object> オブジェクトの各行のデータを保持します。

画面に現在表示されているリスト アクティビティには、
          23 個。各項目には数値があります。数値は文字列として保存され、
          代わりにオブジェクトとして保存されます。

各マップには 2 つのエントリがあります。1 つのキー "STR" で、 "item: x"、鍵 "LEN"Integer という形式で、 必要があります。例:

{"STR" : "item: 0", "LEN": 7}

「item: 50」の行をクリックするコードは次のようになります。

KotlinJava
onData(allOf(`is`(instanceOf(Map::class.java)), hasEntry(equalTo("STR"),
       
`is`("item: 50")))).perform(click())
onData(allOf(is(instanceOf(Map.class)), hasEntry(equalTo("STR"), is("item: 50"))))
   
.perform(click());

なお、Espresso によりリストは必要に応じて自動的にスクロールされます。

onData() 内の Matcher<Object> を細かく見てみましょう。「 is(instanceOf(Map.class)) メソッドを使用すると、検索対象を AdapterView は、Map オブジェクトを基盤としています。

この例では、クエリのこの側面はリストビューのすべての行に一致しますが、 特定の項目をクリックする必要があるため、次のように検索対象を絞り込みます。

KotlinJava
hasEntry(equalTo("STR"), `is`("item: 50"))
hasEntry(equalTo("STR"), is("item: 50"))

この Matcher<String, Object> は、次を含むエントリを含むすべてのマップに一致します。 キー "STR" と値 "item: 50" です。これを検索するコードは 他の場所で再利用したい場合は そのための withItemContent マッチャー:

KotlinJava
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)
   
}
}
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);
   
}
};

タイプのオブジェクトのみを一致させるため、BoundedMatcher をベースとして使用する。 MapmatchesSafely() メソッドをオーバーライドし、見つかったマッチャーを挿入します。 Matcher<String> 渡します。これにより、withItemContent(equalTo("foo")) を呼び出せるようになります。コード equalTo() を呼び出している別のマッチャーを作成して、 は、String オブジェクトを受け入れます。

KotlinJava
fun withItemContent(expectedText: String): Matcher<Object> {
    checkNotNull
(expectedText)
   
return withItemContent(equalTo(expectedText))
}
public static Matcher<Object> withItemContent(String expectedText) {
    checkNotNull
(expectedText);
   
return withItemContent(equalTo(expectedText));
}

これで、項目をクリックするためのコードは次のように簡単になります。

KotlinJava
onData(withItemContent("item: 50")).perform(click())
onData(withItemContent("item: 50")).perform(click());

このテストの完全なコードについては、testClickOnItem50() メソッドをご覧ください。 AdapterViewTest クラスと このカスタムLongListMatchers matcher をご覧ください。

特定の子ビューのマッチング

上記のサンプルでは、ListView の行全体の中央でクリックを行います。 今度は、行の特定の子要素を操作する場合を考えてみましょう。たとえば、 LongListActivity の行の 2 列目をクリックしようとしています。 これは、最初の列のコンテンツの String.length を表示します。

この例では、テキストの長さだけを抽出して、
          ブロックすることもできます。このプロセスでは
          値を返します。

onChildView() の仕様を DataInteraction:

KotlinJava
onData(withItemContent("item: 60"))
   
.onChildView(withId(R.id.item_size))
   
.perform(click())
onData(withItemContent("item: 60"))
   
.onChildView(withId(R.id.item_size))
   
.perform(click());

リサイクラー ビューのリスト項目の操作

RecyclerView オブジェクトは AdapterView オブジェクトと動作が異なるため、 onData() を使用して操作することはできません。

Espresso を使用して RecyclerView を操作するには、 espresso-contrib パッケージには、次のコレクションが含まれます。 RecyclerViewActions 位置へのスクロールやアイテムに対するアクションを行うために使用できます。

  • scrollTo() - 一致するビューが存在する場合、そのビューまでスクロールします。
  • scrollToHolder() - 一致するビューホルダーが存在する場合は、そのビューホルダーまでスクロールします。
  • scrollToPosition() - 特定の位置までスクロールします。
  • actionOnHolderItem() - 一致するビューホルダーに対してビュー アクションを実行します。
  • actionOnItem() - 一致するビューに対してビュー アクションを実行します。
  • actionOnItemAtPosition() - 特定の位置でビューに対してビュー アクションを実行します。

次のスニペットは、 RecyclerViewSample サンプル:

KotlinJava
@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"))
           
)
       
)
}
@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"))
           
));
}
KotlinJava
@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()))
}
@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()));
}
KotlinJava
@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()))
}
@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: Espresso の onData() エントリ ポイント(リストと AdapterView)を示します。 説明します。