Espresso를 사용하여 RecyclerView 항목 내의 특정보기를 클릭하려면 어떻게 해야합니까? 다음을 사용하여 위치 0에서 항목을 클릭 할 수 있다는 것을 알고 있습니다.
onView(withId(R.id.recyclerView))
.perform(RecyclerViewActions.actionOnItemAtPosition(0, click()));
하지만 항목 자체가 아닌 해당 항목 내부의 특정보기를 클릭해야합니다.
미리 감사드립니다.
— 편집하다 —
더 정확하게 말하자면, CardView ( ) 항목 인 RecyclerView ( R.id.recycler_view
)가 있습니다 . 각 CardView 안에는 4 개의 버튼 (다른 것들 중에서)이 있고 특정 버튼 ( ) 을 클릭하고 싶습니다 .R.id.card_view
R.id.bt_deliver
Espresso 2.0의 새로운 기능을 사용하고 싶지만 이것이 가능한지 잘 모르겠습니다.
가능하지 않다면 다음과 같이 사용하고 싶습니다 (Thomas Keller 코드 사용).
onRecyclerItemView(R.id.card_view, ???, withId(R.id.bt_deliver)).perform(click());
하지만 나는 물음표에 무엇을 써야할지 모르겠습니다.
답변
보기 작업을 사용자 지정하여 수행 할 수 있습니다.
public class MyViewAction {
public static ViewAction clickChildViewWithId(final int id) {
return new ViewAction() {
@Override
public Matcher<View> getConstraints() {
return null;
}
@Override
public String getDescription() {
return "Click on a child view with specified id.";
}
@Override
public void perform(UiController uiController, View view) {
View v = view.findViewById(id);
v.performClick();
}
};
}
}
그런 다음 클릭 할 수 있습니다.
onView(withId(R.id.rv_conference_list)).perform(
RecyclerViewActions.actionOnItemAtPosition(0, MyViewAction.clickChildViewWithId(R.id. bt_deliver)));
답변
이제 android.support.test.espresso.contrib를 사용하면 더 쉬워졌습니다.
1) 테스트 종속성 추가
androidTestCompile('com.android.support.test.espresso:espresso-contrib:2.0') {
exclude group: 'com.android.support', module: 'appcompat'
exclude group: 'com.android.support', module: 'support-v4'
exclude module: 'recyclerview-v7'
}
* 이미 가지고있을 가능성이 높으므로 3 개 모듈 제외
2) 그런 다음 다음과 같이하십시오.
onView(withId(R.id.recycler_grid))
.perform(RecyclerViewActions.actionOnItemAtPosition(0, click()));
또는
onView(withId(R.id.recyclerView))
.perform(RecyclerViewActions.actionOnItem(
hasDescendant(withText("whatever")), click()));
또는
onView(withId(R.id.recycler_linear))
.check(matches(hasDescendant(withText("whatever"))));
답변
다음은 kotlin에서 문제를 해결 한 방법입니다.
fun clickOnViewChild(viewId: Int) = object : ViewAction {
override fun getConstraints() = null
override fun getDescription() = "Click on a child view with specified id."
override fun perform(uiController: UiController, view: View) = click().perform(uiController, view.findViewById<View>(viewId))
}
그리고
onView(withId(R.id.recyclerView)).perform(RecyclerViewActions.actionOnItemAtPosition<RecyclerView.ViewHolder>(position, clickOnViewChild(R.id.viewToClickInTheRow)))
답변
다음 접근 방식을 시도하십시오.
onView(withRecyclerView(R.id.recyclerView)
.atPositionOnView(position, R.id.bt_deliver))
.perform(click());
public static RecyclerViewMatcher withRecyclerView(final int recyclerViewId) {
return new RecyclerViewMatcher(recyclerViewId);
}
public class RecyclerViewMatcher {
final int mRecyclerViewId;
public RecyclerViewMatcher(int recyclerViewId) {
this.mRecyclerViewId = recyclerViewId;
}
public Matcher<View> atPosition(final int position) {
return atPositionOnView(position, -1);
}
public Matcher<View> atPositionOnView(final int position, final int targetViewId) {
return new TypeSafeMatcher<View>() {
Resources resources = null;
View childView;
public void describeTo(Description description) {
int id = targetViewId == -1 ? mRecyclerViewId : targetViewId;
String idDescription = Integer.toString(id);
if (this.resources != null) {
try {
idDescription = this.resources.getResourceName(id);
} catch (Resources.NotFoundException var4) {
idDescription = String.format("%s (resource name not found)", id);
}
}
description.appendText("with id: " + idDescription);
}
public boolean matchesSafely(View view) {
this.resources = view.getResources();
if (childView == null) {
RecyclerView recyclerView =
(RecyclerView) view.getRootView().findViewById(mRecyclerViewId);
if (recyclerView != null) {
childView = recyclerView.findViewHolderForAdapterPosition(position).itemView;
}
else {
return false;
}
}
if (targetViewId == -1) {
return view == childView;
} else {
View targetView = childView.findViewById(targetViewId);
return view == targetView;
}
}
};
}
}
답변
당신은 할 수 클릭 에 3 항목 의 recyclerView
처럼이 :
onView(withId(R.id.recyclerView)).perform(
RecyclerViewActions.actionOnItemAtPosition<RecyclerView.ViewHolder>(2,click()))
추론이 실패하지 않도록 유형 을 제공하는 것을 잊지 마십시오 ViewHolder
.
답변
위의 모든 답변이 나를 위해 작동하지 않았으므로 요청 된 ID로 뷰를 반환하기 위해 셀 내부의 모든 뷰를 검색하는 새로운 방법을 구축했습니다. 두 가지 방법이 필요합니다 (하나로 결합 될 수 있음).
fun performClickOnViewInCell(viewID: Int) = object : ViewAction {
override fun getConstraints(): org.hamcrest.Matcher<View> = click().constraints
override fun getDescription() = "Click on a child view with specified id."
override fun perform(uiController: UiController, view: View) {
val allChildViews = getAllChildrenBFS(view)
for (child in allChildViews) {
if (child.id == viewID) {
child.callOnClick()
}
}
}
}
private fun getAllChildrenBFS(v: View): List<View> {
val visited = ArrayList<View>();
val unvisited = ArrayList<View>();
unvisited.add(v);
while (!unvisited.isEmpty()) {
val child = unvisited.removeAt(0);
visited.add(child);
if (!(child is ViewGroup)) continue;
val group = child
val childCount = group.getChildCount();
for (i in 0 until childCount) { unvisited.add(group.getChildAt(i)) }
}
return visited;
}
마지막으로 다음을 수행하여 Recycler View에서이를 사용할 수 있습니다.
onView(withId(R.id.recyclerView)).perform(actionOnItemAtPosition<RecyclerView.ViewHolder>(0, getViewFromCell(R.id.cellInnerView) {
val requestedView = it
}))
다른 작업을 수행하려는 경우 콜백을 사용하여 뷰를 반환하거나 다른 작업을 수행하기 위해 3-4 개의 다른 버전을 빌드 할 수 있습니다.
답변
나는 @blade의 대답이 나를 위해 작동하지 않는 이유를 찾기 위해 다양한 방법을 계속 시도했고, 내가 가지고 있음을 깨닫기 위해 OnTouchListener()
그에 따라 ViewAction을 수정했습니다.
fun clickTopicToWeb(id: Int): ViewAction {
return object : ViewAction {
override fun getDescription(): String {...}
override fun getConstraints(): Matcher<View> {...}
override fun perform(uiController: UiController?, view: View?) {
view?.findViewById<View>(id)?.apply {
//Generalized for OnClickListeners as well
if(isEnabled && isClickable && !performClick()) {
//Define click event
val event: MotionEvent = MotionEvent.obtain(
SystemClock.uptimeMillis(),
SystemClock.uptimeMillis(),
MotionEvent.ACTION_UP,
view.x,
view.y,
0)
if(!dispatchTouchEvent(event))
throw Exception("Not clicking!")
}
}
}
}
}