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