Android - espresso - 根据自定义对象点击ListView条目

12

Espresso被用于自动测试我的应用。

编辑:下面你会找到一些答案!

如何在一个自动化的Espresso测试脚本中点击一个定制对象列表中的条目?

在Espresso文档中有一个LongList的例子。我通常处理对象列表。尝试从Map到Object的许多选项至今没有产生良好的结果。

Espresso文档说应该使用'onData'。所以,类似这样:

onData( myObjectHasContent("my_item: 50")).perform(click());
onView(withId( R.id.selection_pos2)).check(matches(withText("50")));

我的问题(我认为它们对学习社区非常有帮助): - 你能写一个好的Matcher吗? - 我们如何在“onData”中使用它?

情况是什么?屏幕上有一个对象的ListView,例如:

public class MyOjbect { 
    public String content; 
    public int    size; 
}

我用来填充已填充列表的适配器是:

public class MyObjectWithItemAndSizeAdapter extends ArrayAdapter<MyObjectWithItemAndSize> {
    private final Context context;
    private final List<MyObjectWithItemAndSize> values;
    ...
    @Override
    public View getView(int position, View concertView, ViewGroup parent) {
        View view = null;
        if (concertView != null) {
            view = (LinearLayout) concertView;
        } else {
            LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            view = inflater.inflate( R.layout.list_item, parent, false);
        } 
        TextView itemT = (TextView) view.findViewById( R.id.item_content);
        itemT.setText( values.get(position).item);
        TextView sizeT = (TextView) view.findViewById( R.id.item_size);
        sizeT.setText( "" + values.get(position).size);
        return view;
    }
 }

你可以使用 setOnItemClick 来处理 ListView。 - Piyush
谢谢,这是关于使用Android Espresso进行自动化测试的内容。我更新了我的问题,现在更加清晰明了。 - tm1701
3个回答

21

onData() 函数的匹配器必须匹配所需的 ListViewAdapter.getItem(int) 返回值。

因此,在您的示例中,匹配器应该是这样的:

public static Matcher<Object> withContent(final String content) {
    return new BoundedMatcher<Object, MyObjectWithItemAndSize>(MyObjectWithItemAndSize.class) {
        @Override
        public boolean matchesSafely(MyObjectWithItemAndSize myObj) {
            return myObj.content.equals(content);
        }

        @Override
        public void describeTo(Description description) {
            description.appendText("with content '" + content + "'");
        }
    };
}

8

除了之前的回答,我创建了一个完整版本,其中包括两种验证方式。这可以帮助您了解Espresso和自定义匹配器。

与标准的Espresso LongList示例不同之处在于,我使用自定义对象列表来显示在列表视图中。向右滚动列表条目并检查结果如下所示。

方法1-针对字符串的验证

在测试脚本中是:

onData( allOf( instanceOf( MyObjectWithItemAndSize.class), myCustomObjectShouldHaveString( "my_item: 60")))
         .perform(click());
// testing the result ... as in the longlist example
onView(withId(R.id.selection_pos2)).check(matches(withText("my_item: 60"))); 

匹配器是:
public static Matcher<Object> myCustomObjectShouldHaveString( String expectedTest) {
    return myCustomObjectShouldHaveString( equalTo( expectedTest));
}
private static Matcher<Object> myCustomObjectShouldHaveString(final Matcher<String> expectedObject) {
return new BoundedMatcher<Object, MyObjectWithItemAndSize>( MyObjectWithItemAndSize.class) {
    @Override
    public boolean matchesSafely(final MyObjectWithItemAndSize actualObject) {
        // next line is important ... requiring a String having an "equals" method
        if( expectedObject.matches( actualObject.item) ) {
             return true;
           } else { 
             return false;
           }
      }
      @Override
      public void describeTo(final Description description) {
         // could be improved, of course
         description.appendText("getnumber should return ");
      }
   };
}

方法二:针对(完整对象)进行验证。
在测试脚本中,您会发现:
MyObjectWithItemAndSize myObject = new MyObjectWithItemAndSize( "my_item: 60", 11);
onData( allOf( instanceOf( MyObjectWithItemAndSize.class), myObjectHasContent( myObject))).perform( click());
onView(withId( R.id.selection_pos2)).check(matches(withText("my_item: 60"))); 

匹配器是一种用于搜索字符串中特定模式的工具。它可以在给定的文本中查找与预定义模式匹配的所有实例,并返回这些实例的位置和/或值。
最重要的一行代码(我一直在努力理解)位于// ****下面。
public static Matcher<Object> myObjectHasContent( MyObjectWithItemAndSize expectedObject) {
   return myObjectHasContent( equalTo( expectedObject));
}
//private method that does the work of matching
private static Matcher<Object> myObjectHasContent(final Matcher<MyObjectWithItemAndSize> expectedObject) {
     return new BoundedMatcher<Object, MyObjectWithItemAndSize>(MyObjectWithItemAndSize.class) {
        @Override
        public boolean matchesSafely( final MyObjectWithItemAndSize actualObject) {
            // ****** ... the 'matches'. See below. 
            // this requires the MyObjectWithItemAndSize to have an 'equals' method
            if( expectedObject.matches( actualObject) ) {
                return true;
            } else { 
                return false;
            }
        }
        @Override
        public void describeTo(final Description description) {
           description.appendText("getnumber should return ");
        }
     };
  }

非常重要的是,Custom对象具有此方法(我猜测已经被覆盖):
@Override
public boolean equals( Object mob2) {
    return( (this.item.equals( ((MyObjectWithItemAndSize) mob2).item)));
    // of course, could have also a check on this.size.
} 

它起作用了!!!花了一些时间,但克服了。也感谢Yash F的帮助。


另一种选择是在MyObjectWithItemAndSize类中覆盖toString方法,并与hasToString匹配,例如onData(allOf(is(instanceOf(MyObjectWithItemAndSize.class)), hasToString(containsString("40")))) - David Boho

4

今天我需要使用Espresso 2测试一个使用自定义适配器的AdapterView。最终我使用了FeatureMatcher

private static FeatureMatcher<Product, String> withProductName(final String productName) {
    return new FeatureMatcher<Product, String>(equalTo(productName), "with productName", "productName") {
        @Override
        protected String featureValueOf(Product actual) {
            return actual.name;
        }
    };
}

然后像下面这样从测试中调用此实用方法:

onData(withProductName("My Awesome Product"))
            .inAdapterView(withId(R.id.product_list))
            .onChildView(withId(R.id.product_title))
            .check(matches(withText("My Awesome Product")));

我认为FeatureMatcher在你想要断言数据对象的特定属性时非常好用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接