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!")
}
}
}
}
}