Java 如何在浓缩咖啡中的RecyclerView中进行断言?
我正在使用espresso contrib在Java 如何在浓缩咖啡中的RecyclerView中进行断言?,java,android,android-espresso,Java,Android,Android Espresso,我正在使用espresso contrib在RecyclerView上执行操作,它可以正常工作,例如: //单击第一项 onView(带id(R.id.recycler\U视图)) .perform(RecycleServiceActions.ActionItemPosition(0,单击()); 我需要对它执行断言。大概是这样的: onView(withId(R.id.recycler_view)) .perform( RecyclerViewActions.acti
RecyclerView
上执行操作,它可以正常工作,例如:
//单击第一项
onView(带id(R.id.recycler\U视图))
.perform(RecycleServiceActions.ActionItemPosition(0,单击());
我需要对它执行断言。大概是这样的:
onView(withId(R.id.recycler_view))
.perform(
RecyclerViewActions.actionOnItemAtPosition(
0, check(matches(withText("Test Text")))
)
);
但是,因为,当然,它期望一个动作,所以它表示错误的第二个参数类型。浓缩咖啡contrib上没有RecycleServiceAssertions
是否有任何方法可以在
RecyclerView
上执行断言?您应该查看Danny Roa的解决方案
然后像这样使用它:
onView(withId(R.id.recycler_view))
.perform(
RecyclerViewActions.actionOnItemAtPosition(
0, check(matches(withText("Test Text")))
)
);
onView(带回收器视图(R.id.recycler\u视图)
.AtPositionView(1,您要检查的元件的R.id))
.检查(匹配(与文本(“测试文本”));
非常简单。不需要额外的图书馆。做:
onView(withId(R.id.recycler_view))
.check(matches(atPosition(0, withText("Test Text"))));
如果您的ViewHolder使用ViewGroup,请将withText()
与hasGenerant()换行,如:
onView(withId(R.id.recycler_view))
.check(matches(atPosition(0, hasDescendant(withText("Test Text")))));
使用方法,您可以将其放入Utils
类中
public static Matcher<View> atPosition(final int position, @NonNull final Matcher<View> itemMatcher) {
checkNotNull(itemMatcher);
return new BoundedMatcher<View, RecyclerView>(RecyclerView.class) {
@Override
public void describeTo(Description description) {
description.appendText("has item at position " + position + ": ");
itemMatcher.describeTo(description);
}
@Override
protected boolean matchesSafely(final RecyclerView view) {
RecyclerView.ViewHolder viewHolder = view.findViewHolderForAdapterPosition(position);
if (viewHolder == null) {
// has no item on such position
return false;
}
return itemMatcher.matches(viewHolder.itemView);
}
};
}
为了增强riwnodennyk的回答,如果您的视图持有者是视图组
,而不是像TextView
这样的直接视图
对象,那么您可以添加hasgendant
匹配器来匹配视图组中的TextView
对象。例如:
onView(withId(R.id.recycler_view))
.check(matches(atPosition(0, hasDescendant(withText("First Element")))));
Danny Roa的解决方案很棒,但对于我刚刚滚动到的屏幕外项目,它不起作用。修复程序正在替换此:
View targetView = recyclerView.getChildAt(this.position)
.findViewById(this.viewId);
为此:
View targetView = recyclerView.findViewHolderForAdapterPosition(this.position)
.itemView.findViewById(this.viewId);
。。。在我的案例中,有必要合并Danny Roa和riwnodennyk的解决方案:
onView(withId(R.id.recyclerview))
.perform(RecyclerViewActions.scrollToPosition(80))
.check(matches(atPositionOnView(80, withText("Test Test"), R.id.targetview)));
方法是:
public static Matcher<View> atPositionOnView(final int position, final Matcher<View> itemMatcher,
@NonNull final int targetViewId) {
return new BoundedMatcher<View, RecyclerView>(RecyclerView.class) {
@Override
public void describeTo(Description description) {
description.appendText("has view id " + itemMatcher + " at position " + position);
}
@Override
public boolean matchesSafely(final RecyclerView recyclerView) {
RecyclerView.ViewHolder viewHolder = recyclerView.findViewHolderForAdapterPosition(position);
View targetView = viewHolder.itemView.findViewById(targetViewId);
return itemMatcher.matches(targetView);
}
};
}
公共静态匹配器AtPositionView(最终int位置,最终匹配器itemMatcher,
@非空最终int targetViewId){
返回新的BoundedMatcher(RecyclerView.class){
@凌驾
公共无效说明(说明){
description.appendText(“具有视图id”+项目匹配器+”位于“+位置);
}
@凌驾
公共布尔匹配安全(最终循环视图循环视图){
RecyclerView.ViewHolder ViewHolder=RecyclerView.FindViewHolder for AdapterPosition(位置);
View targetView=viewHolder.itemView.findViewById(targetViewId);
返回itemMatcher.matches(targetView);
}
};
}
我一直在努力解决同一个问题,我有一个包含6个项目的回收器视图,然后我必须选择位于位置5的一个,而第5个项目在视图中不可见。
上述解决方案也有效。另外,我知道有点晚了,但觉得值得分享
我用一个简单的解决方案做到了这一点,如下所示:
onView(withId(R.id.recylcer_view))
.perform(RecyclerViewActions.actionOnItem(
hasDescendant(withText("Text of item you want to scroll to")),
click()));
RecycleServiceActions.ActionItem
-
对与viewHolderMatcher
匹配的视图执行ViewAction
滚动RecyclerView
至itemViewMatcher匹配的视图
对匹配的视图执行操作
更多详细信息请访问:此线程非常旧,但我最近扩展了RecycleServiceWaction支持,使其也可用于断言。这允许您在仍然使用视图匹配器的情况下测试屏幕外元素,而无需指定位置
查看这篇文章> 对于所有项目(包括屏幕外)的测试,请使用下面的代码
样本:
fun checkThatRedDotIsGone(itemPosition: Int) {
onView(
withId(R.id.recycler_view)).
check(itemViewMatches(itemPosition, R.id.inside_item_view,
withEffectiveVisibility(Visibility.GONE)))
}
类别:
object RecyclerViewAssertions {
fun itemViewMatches(position: Int, viewMatcher: Matcher<View>): ViewAssertion {
return itemViewMatches(position, -1, viewMatcher)
}
/**
* Provides a RecyclerView assertion based on a view matcher. This allows you to
* validate whether a RecyclerView contains a row in memory without scrolling the list.
*
* @param viewMatcher - an Espresso ViewMatcher for a descendant of any row in the recycler.
* @return an Espresso ViewAssertion to check against a RecyclerView.
*/
fun itemViewMatches(position: Int, @IdRes resId: Int, viewMatcher: Matcher<View>): ViewAssertion {
assertNotNull(viewMatcher)
return ViewAssertion { view, noViewException ->
if (noViewException != null) {
throw noViewException
}
assertTrue("View is RecyclerView", view is RecyclerView)
val recyclerView = view as RecyclerView
val adapter = recyclerView.adapter
val itemType = adapter!!.getItemViewType(position)
val viewHolder = adapter.createViewHolder(recyclerView, itemType)
adapter.bindViewHolder(viewHolder, position)
val targetView = if (resId == -1) {
viewHolder.itemView
} else {
viewHolder.itemView.findViewById(resId)
}
if (viewMatcher.matches(targetView)) {
return@ViewAssertion // Found a matching view
}
fail("No match found")
}
}
对象回收服务组件{
有趣的itemViewMatches(位置:Int,viewMatcher:Matcher):ViewAssertion{
return itemViewMatches(位置-1,viewMatcher)
}
/**
*提供基于视图匹配器的RecyclerView断言。这允许您
*在不滚动列表的情况下验证RecyclerView是否在内存中包含行。
*
*@param viewMatcher-回收器中任何行的后代的浓缩咖啡viewMatcher。
*@返回Espresso ViewAssertion以对照RecyclerView进行检查。
*/
有趣的itemViewMatches(位置:Int,@idresid:Int,viewMatcher:Matcher):ViewAssertion{
assertNotNull(视图匹配器)
返回ViewAssertion{view,NovieweException->
if(noViewException!=null){
抛出新的例外
}
assertTrue(“视图为RecyclerView”,视图为RecyclerView)
val recyclerView=查看为recyclerView
val adapter=recyclerView.adapter
val itemType=适配器!!.getItemViewType(位置)
val viewHolder=adapter.createViewHolder(recyclerView,itemType)
适配器。bindViewHolder(viewHolder,位置)
val targetView=if(剩余=1){
viewHolder.itemView
}否则{
viewHolder.itemView.findViewById(剩余)
}
if(viewMatcher.matches(targetView)){
return@ViewAssertion//找到了一个匹配的视图
}
失败(“未找到匹配项”)
}
}
}
基于本文的解决方案>随着项目的发展,我将上面的一些答案组合到一个可重用的方法中,用于测试其他回收者视图。将代码留在这里,以防它会帮助其他人
声明可在RecyclerView资源ID上调用的函数:
fun Int.shouldHaveTextAtPosition(text:String, position: Int) {
onView(withId(this))
.perform(scrollToPosition<RecyclerView.ViewHolder>(position))
.check(matches(atPosition(position, hasDescendant(withText(text)))))
}
上面的解决方案非常好,我在Kotlin又贡献了一个
浓缩咖啡需要滚动到该位置,然后进行检查。要滚动到该位置,我正在使用。然后使用来执行检查。我没有直接调用findviewolderforadapterposition
,而是使用了一个更方便的函数
在下面的示例代码中,Espresso滚动到RecyclerView
的第43行,检查该行的ViewHolder
是否有两个文本视图,以及这些文本视图是否分别包含文本“第43行的hello text 1”和“第43行的hello text 2”:
import androidx.test.espresso.contrib.RecyclerViewActions
import it.xabaras.android.espresso.recyclerviewchildactions.RecyclerViewChildActions.Companion.childOfViewAtPositionWithMatcher
// ...
val index = 42 // index is 0 based, corresponds to row 43 from user's perspective
onView(withId(R.id.myRecyclerView))
.perform(RecyclerViewActions.scrollToPosition<RecyclerView.ViewHolder>(43))
.check(
matches(
allOf(
childOfViewAtPositionWithMatcher(
R.id.myTextView1,
index,
withText("hello text 1 at line 43")
),
childOfViewAtPositionWithMatcherMy(
R.id.myTextView2,
index,
withText("hello text 2 at line 43")
)
)
)
)
当您检查您的项目(文本视图中的文本)是否在RecyclerView中处于第一位时,修改了matcher解决方案
// True if first item in the RV and input text
fun <T> customMatcherForFirstItem(name: String, matcher: Matcher<T>): Matcher<T> {
return object: BaseMatcher<T> () {
var isFirst = true
override fun matches(item: Any): Boolean {
if (isFirst && (item as TextView).text == name) {
isFirst = false
return true
}
return false
}
override fun describeTo(description: Description?) {}
}
}
基于Kotlin用户的->:
我知道我已经很晚了,我很困惑为什么人们要自定义RecycleView操作。我有
androidTestImplementation "androidx.test.espresso:espresso-contrib:3.2.0"
androidTestImplementation "it.xabaras.android.espresso:recyclerview-child-actions:1.0"
// True if first item in the RV and input text
fun <T> customMatcherForFirstItem(name: String, matcher: Matcher<T>): Matcher<T> {
return object: BaseMatcher<T> () {
var isFirst = true
override fun matches(item: Any): Boolean {
if (isFirst && (item as TextView).text == name) {
isFirst = false
return true
}
return false
}
override fun describeTo(description: Description?) {}
}
}
onView(withText("TEXT_VIEW_TEXT")).check(matches(
customMatcherForFirstItem("TEXT_VIEW_TEXT", withParent(withId(R.id.recycler_view)))))
onView(withId(R.id.recycler_view))
.perform(actionOnItemAtPosition<RecyclerView.ViewHolder>(46, scrollTo()))
.check(matches(hasDescendant(withText("TextToMatch"))))