How to go about checking whether RecyclerView items are displayed in the correct order using Espresso? I'm trying to test it checking it by the text for the title of each element.
When I try this piece of code it works to click the element but can't go on to instead of performing a click trying to Assert the text for the element
onView(withId(R.id.rv_metrics)).perform(actionOnItemAtPosition(0, click()));
When I try to use a custom matcher instead I keep getting the error
Error performing 'load adapter data' on view 'with id: mypackage_name:id/rv_metrics'
I know now onData doesn't work for RecyclerView but before that I was trying to use a custom matcher for this task.
public static Matcher<Object> hasTitle(final String inputString) {
return new BoundedMatcher<Object, Metric>(Metric.class) {
#Override
protected boolean matchesSafely(Metric metric) {
return inputString.equals(metric.getMetric());
}
#Override
public void describeTo(org.hamcrest.Description description) {
description.appendText("with title: ");
}
};
}
I also tried something like this but it obviously doesn't work due to the type given as parameter to the actionOnItemAtPosition method but would we have something similar to it that could maybe work?
onView(withId(R.id.rv_metrics)).check(actionOnItemAtPosition(0, ViewAssertions.matches(withText("Weight"))));
What am I missing here please?
Thanks a lot.
As it's been mentioned here, RecyclerView objects work differently than AdapterView objects, so onData() cannot be used to interact with them.
In order to find a view at specific position of a RecyclerView you need to implement a custom RecyclerViewMatcher like below:
public class RecyclerViewMatcher {
private final int recyclerViewId;
public RecyclerViewMatcher(int recyclerViewId) {
this.recyclerViewId = 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) {
String idDescription = Integer.toString(recyclerViewId);
if (this.resources != null) {
try {
idDescription = this.resources.getResourceName(recyclerViewId);
} catch (Resources.NotFoundException var4) {
idDescription = String.format("%s (resource name not found)",
new Object[] { Integer.valueOf
(recyclerViewId) });
}
}
description.appendText("with id: " + idDescription);
}
public boolean matchesSafely(View view) {
this.resources = view.getResources();
if (childView == null) {
RecyclerView recyclerView =
(RecyclerView) view.getRootView().findViewById(recyclerViewId);
if (recyclerView != null && recyclerView.getId() == recyclerViewId) {
childView = recyclerView.findViewHolderForAdapterPosition(position).itemView;
}
else {
return false;
}
}
if (targetViewId == -1) {
return view == childView;
} else {
View targetView = childView.findViewById(targetViewId);
return view == targetView;
}
}
};
}
}
And then use it in your test case in this way:
#Test
void testCase() {
onView(new RecyclerViewMatcher(R.id.rv_metrics)
.atPositionOnView(0, R.id.txt_title))
.check(matches(withText("Weight")))
.perform(click());
onView(new RecyclerViewMatcher(R.id.rv_metrics)
.atPositionOnView(1, R.id.txt_title))
.check(matches(withText("Height")))
.perform(click());
}
If somebody is interested in the Kotlin version, here it is
fun hasItemAtPosition(position: Int, matcher: Matcher<View>) : Matcher<View> {
return object : BoundedMatcher<View, RecyclerView>(RecyclerView::class.java) {
override fun describeTo(description: Description?) {
description?.appendText("has item at position $position : ")
matcher.describeTo(description)
}
override fun matchesSafely(item: RecyclerView?): Boolean {
val viewHolder = item?.findViewHolderForAdapterPosition(position)
return matcher.matches(viewHolder?.itemView)
}
}
}
I simplified a bit Mosius answer:
public static Matcher<View> hasItemAtPosition(final Matcher<View> matcher, final int position) {
return new BoundedMatcher<View, RecyclerView>(RecyclerView.class) {
#Override
public void describeTo(Description description) {
description.appendText("has item at position " + position + ": ");
matcher.describeTo(description);
}
#Override
protected boolean matchesSafely(RecyclerView recyclerView) {
RecyclerView.ViewHolder viewHolder = recyclerView.findViewHolderForAdapterPosition(position);
return matcher.matches(viewHolder.itemView);
}
};
}
We pass Matcher to the function so we can provide further conditions. Example usage:
onView(hasItemAtPosition(hasDescendant(withText("Item 1")), 0)).check(matches(isDisplayed()));
onView(hasItemAtPosition(hasDescendant(withText("Item 2")), 1)).check(matches(isDisplayed()));
The original problem has been solved but am posting an answer here as found the Barista library solves this problem in one single line of code.
assertDisplayedAtPosition(R.id.rv_metrics, 0, R.id.tv_title, "weight");
It's made on top of Espresso and the documentation for it can be found here
Hope this may be helpful to someone. :)
If you want to match a matcher on a position in RecyclerView, then you can try to create a custom Matcher<View>:
public static Matcher<View> hasItemAtPosition(int position, Matcher<View> matcher) {
return new BoundedMatcher<View, RecyclerView>(RecyclerView.class) {
#Override public void describeTo(Description description) {
description.appendText("has item: ");
matcher.describeTo(description);
description.appendText(" at position: " + position);
}
#Override protected boolean matchesSafely(RecyclerView view) {
RecyclerView.Adapter adapter = view.getAdapter();
int type = adapter.getItemViewType(position);
RecyclerView.ViewHolder holder = adapter.createViewHolder(view, type);
adapter.onBindViewHolder(holder, position);
return matcher.matches(holder.itemView);
}
};
}
And you can use it for example:
onView(withId(R.id.rv_metrics)).check(matches(0, hasDescendant(withText("Weight")))))
Related
I have added a checkbox and the paging library to Google's RoomWithAView codelab and the call to DiffUtil.ItemCallback seems to be passing the updated version of the entity to both the oldItem and newItem parameters.
My checkbox checked state is driven by a boolean field in the database named "isSelected" which gets updated when the row is clicked and this should cause the checkbox state to change.
The problem is that when I update the "isSelected" field (from false to true for example), the following Log printing returns true for both items. My checkbox state doesn't change because areContentsTheSame returns true and onBindViewHolder isn't called. I can force this to return false, but I want to understand what is going wrong:
private static DiffUtil.ItemCallback<WordEntity> DIFF_CALLBACK =
new DiffUtil.ItemCallback<WordEntity>() {
#Override
public boolean areItemsTheSame(WordEntity oldItem, WordEntity newItem) {
Log.i("CLEAN_LOG","areItemsTheSame: " +
Boolean.toString(oldItem.getWordId()==newItem.getWordId()));
return oldItem.getWordId() == newItem.getWordId();
}
#Override
public boolean areContentsTheSame(WordEntity oldItem, WordEntity newItem) {
Log.i("CLEAN_LOG","oldItem: " +
Boolean.toString(oldItem.getIsSelected()));
Log.i("CLEAN_LOG","newItem: " +
Boolean.toString(newItem.getIsSelected()));
Log.i("CLEAN_LOG","areContentsTheSame: " +
Boolean.toString(oldItem.getIsSelected() == newItem.getIsSelected()));
return oldItem.getIsSelected() == newItem.getIsSelected();
}
};
Here is my PagedListAdapter:
public static class WordListAdapter extends PagedListAdapter<WordEntity, WordListAdapter.WordViewHolder> {
protected WordListAdapter() {
super(DIFF_CALLBACK);
setHasStableIds(true);
}
#NonNull
#Override
public WordViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.recyclerview_item, parent, false);
return new WordViewHolder(itemView);
}
#Override
public void onBindViewHolder(#NonNull WordViewHolder holder, int position) {
WordEntity current = getItem(position);
if (current != null) {
holder.bindTo(current);
}
}
private static DiffUtil.ItemCallback<WordEntity> DIFF_CALLBACK =
new DiffUtil.ItemCallback<WordEntity>() {
#Override
public boolean areItemsTheSame(WordEntity oldItem, WordEntity newItem) {
Log.i("CLEAN_LOG","areItemsTheSame: " +
Boolean.toString(oldItem.getWordId()==newItem.getWordId()));
return oldItem.getWordId() == newItem.getWordId();
}
#Override
public boolean areContentsTheSame(WordEntity oldItem, WordEntity newItem) {
Log.i("CLEAN_LOG","oldItem: " +
Boolean.toString(oldItem.getIsSelected()));
Log.i("CLEAN_LOG","newItem: " +
Boolean.toString(newItem.getIsSelected()));
Log.i("CLEAN_LOG","areContentsTheSame: " +
Boolean.toString(oldItem.getIsSelected() == newItem.getIsSelected()));
return oldItem.getIsSelected() == newItem.getIsSelected();
}
};
#Override
public long getItemId(int position) {
WordEntity current = getItem(position);
return current.mWordId;
}
class WordViewHolder extends RecyclerView.ViewHolder {
TextView wordItemView;
CheckBox checkBox;
LinearLayout viewForeground;
public void bindTo(WordEntity word) {
wordItemView.setText(word.mWord);
checkBox.setChecked(word.mIsSelected);
}
private WordViewHolder(View itemView) {
super(itemView);
viewForeground = itemView.findViewById(R.id.viewForeground);
wordItemView = itemView.findViewById(R.id.textView);
checkBox = itemView.findViewById(R.id.checkBox);
checkBox.setClickable(false);
viewForeground.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View view) {
final WordEntity thisWord = getItem(getAdapterPosition());
if (thisWord != null) {
Toast.makeText(context,
"You long-clicked: " + thisWord.getWord(),
Toast.LENGTH_LONG).show();
}
// returning false here will alow onClickListener to trigger as well
return true;
}
});
viewForeground.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
final WordEntity thisWord = getItem(getAdapterPosition());
if (thisWord != null) {
if (thisWord.getIsSelected()) {
thisWord.setIsSelected(false);
} else {
thisWord.setIsSelected(true);
}
mWordViewModel.update(thisWord);
}
}
});
}
}
}
Here is my observer:
mWordViewModel = ViewModelProviders.of(this).get(WordViewModel.class);
mWordViewModel.getAllWords().observe(this, new Observer<PagedList<WordEntity>>() {
#Override
public void onChanged(#Nullable final PagedList<WordEntity> words) {
// Update the cached copy of the words in the adapter.
adapter.submitList(words);
if (words != null) {
wordCount = words.size();
} else {
wordCount = 0;
}
Log.i(LOG_TAG,"Word Count: " + Integer.toString(wordCount));
}
});
All the Room database updates are happening properly
Based on the Log, it seems that areItemsTheSame is getting called twice when a row is tapped and areContentsTheSame is getting called once
I was expecting oldItem.getIsSelected() to be false and new.Item.getIsSelected() to be true and then onBindViewHolder would be fired. I also expected areItemsTheSame and areContentsTheSame to only get called once for each item.
Can someone help me understand what is going wrong here and if my expectations are in line with what should be happening?
Here is a GitHub with my sample app:
https://github.com/DanglaGT/RoomWithAViewPaging
I meet the same program with you, thanks to this answer, I finally find that to use the Paging Library, you need to make sure that the PagedList and it's item BOTH a new object from the old one.
There is my solution:
if (thisWord.getIsSelected()) {
thisWord.setIsSelected(false);
} else {
thisWord.setIsSelected(true);
}
WordEntity newWord = copyNewObject(thisWord);
mWordViewModel.update(newWord);
I need to make an instrumented test using espresso or uiautomator (I'm open to other sugestions) to validate the existence of a value based on another value in a list.
The questions/anwsers I have seen, all have solutions regarding the using of an index from the list view, or using a specific tag with a number associated (which is basicaly the same thing)
My problem is that I don't know the position, but I need to check that when I encounter a specific String value, that on that row, an image is showing.
The list are populated by a Custom Adapter.
Got any ideas?
After several attemps I end up doing some custom matcher that with a few alterations should work for all kind of views.
Here's how it's called in the test:
onView(CommonMatchers.withTextAndComparableText(R.id.tvTypeName, R.id.rlParentView, R.id.tvName, is(typeNameText), is (nameText))).check(matches(isDisplayed()));
variables:
childId - view that I want to compare from,
parentId - parent of both views,
comparableChildId - view that I want to compare to
Here's the Matcher:
public static Matcher<View> withTextAndComparableText(int childId, int parentId, int comparableChildId,
final Matcher<String>
eventMatcher,
final Matcher<String> parcelIdMatcher) {
checkNotNull(eventMatcher);
return new BoundedMatcher<View, TextView>(TextView.class) {
#Override
public void describeTo(Description description) {
description.appendText("with text: ");
eventMatcher.describeTo(description);
}
#Override
public boolean matchesSafely(TextView textView) {
if (eventMatcher.matches(textView.getText().toString()) && childId == textView.getId()) {
TextView textViewToCompare = (TextView) checkParentAndComparableChildForValidation(parentId,
comparableChildId, textView, parcelIdMatcher);
return textViewToCompare != null && parcelIdMatcher.matches(textViewToCompare.getText().toString());
}
return false;
}
};
}
Then add the next private methods to first get the parent view and then the comprable child view
private static View checkParentAndComparableChildForValidation(int parentId, int comparableChildId,
View objectView,
Matcher<String> parcelIdMatcher) {
ViewParent parentView = findParentRecursively(objectView, parentId);
Object childObject = getObjectFromParent((View) parentView, comparableChildId);
if (childObject instanceof View) {
return (View) childObject;
}
return null;
}
private static Object getObjectFromParent(View viewParent, int childId) {
return viewParent.findViewById(childId);
}
private static ViewParent findParentRecursively(View view, int targetId) {
if (view.getId() == targetId) {
return (ViewParent) view;
}
View parent = (View) view.getParent();
if (parent == null) {
return null;
}
return findParentRecursively(parent, targetId);
}
And voilá!
If you want to compare with an Image or other view, there's few alterations to be done...
public static Matcher<View> withImageAndComparableText(int childId, int parentId, int comparableChildId,
final Matcher<String> parcelIdMatcher) {
return new BoundedMatcher<View, ImageView>(ImageView.class) {
#Override
public void describeTo(Description description) {
description.appendText("with id: " + childId);
}
#Override
public boolean matchesSafely(ImageView imageView) {
if (imageView.getId() == childId) {
TextView textViewToCompare = (TextView) checkParentAndComparableChildForValidation(parentId,
comparableChildId, imageView, parcelIdMatcher);
return textViewToCompare != null && parcelIdMatcher.matches(textViewToCompare.getText().toString());
}
return false;
}
};
}
and the call in the tests:
onView(CommonMatchers.withImageAndComparableText(imageId, R.id.rlParentView, R.id.name, is(parcelId)))
.check(matches(isDisplayed()));
Hope it helps
For the given RecyclerView, I can access the LinearLayout element by:
recyclerView.findViewHolderForAdapterPosition(1)
Where 1 is the 2nd element/position within RecyclerView and the ViewHolder is returned.
How can I then access the child elements of the ViewHolder LinearLayout? Or is there a way to get to the AppCompatTextView element so that I can pull the text from that element?
I have created RecyclerViewMatcher class then following code
onView(withRecyclerView(R.id.rv_fragment_recipes).atPosition(0))
.check(matches(isDisplayed()));
onView(withRecyclerView(R.id.rv_fragment_recipes).atPositionOnView(0, R.id.tv_recipe_name))
.check(matches(isDisplayed()))
.check(matches(withText("Nutella Pie")));
onView(withRecyclerView(R.id.rv_fragment_recipes).atPositionOnView(0, R.id.tv_servings))
.check(matches(isDisplayed()))
.check(matches(withText("Servings : 8")));
onView(withRecyclerView(R.id.rv_fragment_recipes).atPosition(0))
.perform(click());
public class RecyclerViewMatcher {
private final int recyclerViewId;
public RecyclerViewMatcher(int recyclerViewId) {
this.recyclerViewId = 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) {
String idDescription = Integer.toString(recyclerViewId);
if (this.resources != null) {
try {
idDescription = this.resources.getResourceName(recyclerViewId);
} catch (Resources.NotFoundException var4) {
idDescription = String.format("%s (resource name not found)",
new Object[] { Integer.valueOf
(recyclerViewId) });
}
}
description.appendText("with id: " + idDescription);
}
public boolean matchesSafely(View view) {
this.resources = view.getResources();
if (childView == null) {
RecyclerView recyclerView =
(RecyclerView) view.getRootView().findViewById(recyclerViewId);
if (recyclerView != null && recyclerView.getId() == recyclerViewId) {
childView = recyclerView.findViewHolderForAdapterPosition(position).itemView;
}
else {
return false;
}
}
if (targetViewId == -1) {
return view == childView;
} else {
View targetView = childView.findViewById(targetViewId);
return view == targetView;
}
}
};
}
}
for refernce you can follow this project
Use IdelingResource if recyclerView data is not static
If I have an "AppCompatTextView" element that I can access by:
onView(withId(R.id.allergies_text))
From Layout Inspector:
Is there a way I can access the text of the element in Android Studio? (to access whatever text is there... not check if some text exists in the element)
I tried to do:
val tv = onView(withId(R.id.medical_summary_text_view)) as TextView
val text = text.text.toString()
print(text)
But I get the error:
android.support.test.espresso.ViewInteraction cannot be cast to android.widget.TextView
You should create a matcher to access to that element value.
For instance, you can check if it's text has some value:
Matcher<View> hasValueEqualTo(final String content) {
return new TypeSafeMatcher<View>() {
#Override
public void describeTo(Description description) {
description.appendText("Has EditText/TextView the value: " + content);
}
#Override
public boolean matchesSafely(View view) {
if (!(view instanceof TextView) && !(view instanceof EditText)) {
return false;
}
if (view != null) {
String text;
if (view instanceof TextView) {
text = ((TextView) view).getText().toString();
} else {
text = ((EditText) view).getText().toString();
}
return (text.equalsIgnoreCase(content));
}
return false;
}
};
}
And call it this way:
onView(withId(R.id.medical_summary_text_view))
.check(matches(hasValueEqualTo(value)));
or you can edit this matcher to return just whether the text is empty or not:
Matcher<View> textViewHasValue() {
return new TypeSafeMatcher<View>() {
#Override
public void describeTo(Description description) {
description.appendText("The TextView/EditText has value");
}
#Override
public boolean matchesSafely(View view) {
if (!(view instanceof TextView) && !(view instanceof EditText)) {
return false;
}
if (view != null) {
String text;
if (view instanceof TextView) {
text = ((TextView) view).getText().toString();
} else {
text = ((EditText) view).getText().toString();
}
return (!TextUtils.isEmpty(text));
}
return false;
}
};
}
And call it this way:
onView(withId(R.id.medical_summary_text_view))
.check(matches(textViewHasValue()));
You can get the text of the ViewInteraction by the following function:
fun getText(matcher: ViewInteraction): String {
var text = String()
matcher.perform(object : ViewAction {
override fun getConstraints(): Matcher<View> {
return isAssignableFrom(TextView::class.java)
}
override fun getDescription(): String {
return "Text of the view"
}
override fun perform(uiController: UiController, view: View) {
val tv = view as TextView
text = tv.text.toString()
}
})
return text
}
val numberResult: ViewInteraction = onView(withId(R.id.txNumberResult))
var searchText = getText(numberResult)
I faced a similar issue and this is what ended up working for me:
if you have an activity rule
var activityRule = ActivityTestRule(MainActivity::class.java, true, false)
then you can do something like this:
activityRule.launchActivity(null)
val textView: TextView = activityRule.activity.findViewById(R.id.some_text_view)
val text = textView.text
I think this may be more along the lines of what the original poster was looking for.
When you really want to have the text and not only match it with another value or empty, I post the full final working solution in Java (Not Kotlin) based on the algoritghm of #Mesut GUNES
public class TextHelpers {
public static String getText(ViewInteraction matcher){
final String[] text = new String[1];
ViewAction va = new ViewAction() {
#Override
public Matcher<View> getConstraints() {
return isAssignableFrom(TextView.class);
}
#Override
public String getDescription(){
return "Text of the view";
}
#Override
public void perform(UiController uiController,View view) {
TextView tv = (TextView) view;
text[0] = tv.getText().toString();
}
};
matcher.perform(va);
return text[0];
}
}
So, in your test you can call it like that:
TextHelpers.getText(Espresso.onView(withId(R.id.element)));
It works for all controls extending TextView, so it does on EditText too.
Using Espresso and Hamcrest,
How can I count items number available in a recyclerView?
Exemple: I would like check if 5 items are displaying in a specific RecyclerView (scrolling if necessary).
Here an example ViewAssertion to check RecyclerView item count
public class RecyclerViewItemCountAssertion implements ViewAssertion {
private final int expectedCount;
public RecyclerViewItemCountAssertion(int expectedCount) {
this.expectedCount = expectedCount;
}
#Override
public void check(View view, NoMatchingViewException noViewFoundException) {
if (noViewFoundException != null) {
throw noViewFoundException;
}
RecyclerView recyclerView = (RecyclerView) view;
RecyclerView.Adapter adapter = recyclerView.getAdapter();
assertThat(adapter.getItemCount(), is(expectedCount));
}
}
and then use this assertion
onView(withId(R.id.recyclerView)).check(new RecyclerViewItemCountAssertion(5));
I have started to write an library which should make testing more simple with espresso and uiautomator. This includes tooling for RecyclerView action and assertions. https://github.com/nenick/espresso-macchiato See for example EspRecyclerView with the method assertItemCountIs(int)
Adding a bit of syntax sugar to the #Stephane's answer.
public class RecyclerViewItemCountAssertion implements ViewAssertion {
private final Matcher<Integer> matcher;
public static RecyclerViewItemCountAssertion withItemCount(int expectedCount) {
return withItemCount(is(expectedCount));
}
public static RecyclerViewItemCountAssertion withItemCount(Matcher<Integer> matcher) {
return new RecyclerViewItemCountAssertion(matcher);
}
private RecyclerViewItemCountAssertion(Matcher<Integer> matcher) {
this.matcher = matcher;
}
#Override
public void check(View view, NoMatchingViewException noViewFoundException) {
if (noViewFoundException != null) {
throw noViewFoundException;
}
RecyclerView recyclerView = (RecyclerView) view;
RecyclerView.Adapter adapter = recyclerView.getAdapter();
assertThat(adapter.getItemCount(), matcher);
}
}
Usage:
import static your.package.RecyclerViewItemCountAssertion.withItemCount;
onView(withId(R.id.recyclerView)).check(withItemCount(5));
onView(withId(R.id.recyclerView)).check(withItemCount(greaterThan(5)));
onView(withId(R.id.recyclerView)).check(withItemCount(lessThan(5)));
// ...
To complete nenick answer and provide and little bit more flexible solution to also test if item cout is greaterThan, lessThan ...
public class RecyclerViewItemCountAssertion implements ViewAssertion {
private final Matcher<Integer> matcher;
public RecyclerViewItemCountAssertion(int expectedCount) {
this.matcher = is(expectedCount);
}
public RecyclerViewItemCountAssertion(Matcher<Integer> matcher) {
this.matcher = matcher;
}
#Override
public void check(View view, NoMatchingViewException noViewFoundException) {
if (noViewFoundException != null) {
throw noViewFoundException;
}
RecyclerView recyclerView = (RecyclerView) view;
RecyclerView.Adapter adapter = recyclerView.getAdapter();
assertThat(adapter.getItemCount(), matcher);
}
}
Usage:
onView(withId(R.id.recyclerView)).check(new RecyclerViewItemCountAssertion(5));
onView(withId(R.id.recyclerView)).check(new RecyclerViewItemCountAssertion(greaterThan(5));
onView(withId(R.id.recyclerView)).check(new RecyclerViewItemCountAssertion(lessThan(5));
// ...
Validated answer works but we can solve this problem with one line and without adapter awareness :
onView(withId(R.id.your_recycler_view_id)).check(matches(hasChildCount(2)))
Replace your_recycler_view_id with your id and 2 with the number to assert.
Based on #Sivakumar Kamichetty answer:
Variable 'COUNT' is accessed from within inner class, needs to be declared final.
Unnecessarily line: COUNT = 0;
Transfer COUNT variable to one element array.
Variable result is unnecessary.
Not nice, but works:
public static int getCountFromRecyclerView(#IdRes int RecyclerViewId) {
final int[] COUNT = {0};
Matcher matcher = new TypeSafeMatcher<View>() {
#Override
protected boolean matchesSafely(View item) {
COUNT[0] = ((RecyclerView) item).getAdapter().getItemCount();
return true;
}
#Override
public void describeTo(Description description) {}
};
onView(allOf(withId(RecyclerViewId),isDisplayed())).check(matches(matcher));
return COUNT[0];
}
I used the below method to get the count of RecyclerView
public static int getCountFromRecyclerView(#IdRes int RecyclerViewId) {
int COUNT = 0;
Matcher matcher = new TypeSafeMatcher<View>() {
#Override
protected boolean matchesSafely(View item) {
COUNT = ((RecyclerView) item).getAdapter().getItemCount();
return true;
}
#Override
public void describeTo(Description description) {
}
};
onView(allOf(withId(RecyclerViewId),isDisplayed())).check(matches(matcher));
int result = COUNT;
COUNT = 0;
return result;
}
Usage -
int itemsCount = getCountFromRecyclerView(R.id.RecyclerViewId);
Then perform assertions to check if the itemsCount is as expected
You can create a custom BoundedMatcher:
object RecyclerViewMatchers {
#JvmStatic
fun hasItemCount(itemCount: Int): Matcher<View> {
return object : BoundedMatcher<View, RecyclerView>(
RecyclerView::class.java) {
override fun describeTo(description: Description) {
description.appendText("has $itemCount items")
}
override fun matchesSafely(view: RecyclerView): Boolean {
return view.adapter.itemCount == itemCount
}
}
}
}
And then use it like this:
onView(withId(R.id.recycler_view)).check(matches((hasItemCount(5))))
count with ActivityScenarioRule
#get: Rule
val activityScenarioRule = ActivityScenarioRule(ShowListActivity::class.java)
#Test
fun testItemCount(){
activityScenarioRule.scenario.onActivity { activityScenarioRule ->
val recyclerView = activityScenarioRule.findViewById<RecyclerView(R.id.movieListRecyclerView)
val itemCount = recyclerView.adapter?.itemCount?:0
....
}
}