Espresso - check if the TextView exists in the ListView - android

I want to check displaying of Save €XX in the list. Save €XX is a TextView that can be VISIBLE or INVISIBLE. I use JUnit 4 and Espresso 2.2.1.
I tried to check it like this:
onView(withText(startsWith("Save"))).check(matches(isDisplayed()));
but always get an error:
android.support.test.espresso.AmbiguousViewMatcherException: 'with text: a string starting with "Save"' matches multiple views in the hierarchy.
Is there a way to if the TextView exists in the ListView with Espresso?
UPDATE
I also tried to use onData:
onData(hasToString(startsWith("Save")))
.inAdapterView(withId(R.id.suggestion_list_view)).atPosition(0)
.check(matches(isDisplayed()));
but it seems that onData works with data layer but not the view layer. Therefore, I receive the error:
java.lang.RuntimeException: No data found matching: with toString() a string starting with "Save" contained values: <[Data: ...]>

After several tries, I found the way.
In this case, we should use a combined approach and work with both data and view layers. We access the ListView by ID and choose the first item. Then check it for the 'Save' text.
onData(anything())
.inAdapterView(withId(R.id.list_view))
.atPosition(0)
.onChildView(withId(R.id.suggestion_saving))
.check(matches(withText(startsWith("Save"))));
Works like a charm. Enjoy!

Related

How to access a element by class name within a parent element with R.id

I'm writing an automated test for logging in to an Android app. I'm recording tests with Record Espresso Test and then edit the code as it's usually full of bugs.
I'm using espresso
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2',
and uiAutomatorViewer to double check the R.id's~ and class names.
I've encountered a problem while trying to edit the text in the element with no R.id but with class name android.widget.EditText:
android.support.test.espresso.NoMatchingViewException: No views in hierarchy found matching: (with id: com.mydosesmart:id/til_name and an instance of android.widget.FrameLayout and an instance of android.widget.EditText)
Problem is that the element with class name android.widget.EditText has no R.id.. This class name is not unique for this view, this element with class name android.widget.EditText has a parent element with a unique R.id..
On the login view in the app, two elements have the class name android.widget.EditText therefore I can't call this element just by class name. I want to call it like that:
within the element with R.id.til_name find the element with the class name android.widget.EditText. Below is the code I'm using now and it fails.
ViewInteraction textInputEditText2 = onView(
allOf(withId(R.id.til_name), instanceOf(Class.forName("android.widget.FrameLayout")), instanceOf(Class.forName("android.widget.EditText"))));
textInputEditText2.perform(replaceText("testespresso"), closeSoftKeyboard());
That fails too:
ViewInteraction textInputEditText2 = onView(
allOf(withId(R.id.til_name), instanceOf(Class.forName("android.widget.EditText"))));
textInputEditText2.perform(replaceText("testespresso"), closeSoftKeyboard());
As plenty of elements in the app I'm testing have no designated R.id I would like to find an easy way to call them for testing purposes.
After trying dozens of different matchers in all possible combinations I've found the answer to my question. So far it seems to be universal:
onView(allOf(withClassName(containsString(EditText.class.getSimpleName())), isDescendantOfA(withId(R.id.til_name))))
.perform(replaceText("testespresso "), closeSoftKeyboard());
Using isDescendantOfA we don't need to worry if the element we are looking for has a parent/grandparent with R.id, it just needs to be lower in a hierarchy.
I think you should try this (to find your EditText with no ID but with parent with unique known id) :
allOf(withParent(withId(R.id...)), withClassName(containsString(EditText.class.getName())))
Update based on new info: to match with indirect parent (R.id.... is where to place indirect parent's id) :
allOf(isDescendantOfA(withId(R.id...)), withClassName(containsString(EditText.class.getName())))

Android espresso accessing EditText in adapter in first row

I am using Espresso for testing my app. I have a listview with some data in it. I want to check the value of an edittext in the first line.
I tried:
onData(withId(R.id.editTextKommissioniert)).inAdapterView(withId(R.id.jflArticleList_ListView)).atPosition(0).check(matches(withText("60.0")));
But I always get this exception:
android.support.test.espresso.AmbiguousViewMatcherException: 'with id:
at.stockserv:id/editTextKommissioniert' matches multiple views in the
hierarchy.
What can I do, to access the first line in my adapter?
Try below approach , Working for me
onData(anything()).inAdapterView(withId(R.id.jflArticleList_ListView))
.atPosition(0).onChildView(withId(R.id.edt_id)).check(matches(withText("60.0")));

Espresso, clicking on a item at position

I'm trying to click on a item at a specific position in a grid view.
onData(instanceOf(MyClass.class))
.inAdapterView(withId(R.id.my_view))
.atPosition(R.integer.my_id)
.perform(click());
but I'm getting this java.lang.IndexOutOfBoundsException: Invalid index 0, size is 0
I'm queuing the responses using MockWebServer, even after the UI is on screen with all the list item, I'm getting this error, I'm not sure why.
Also, I want to get the content of the specific item.
Well, I think that's because you're matching class which is only one, not a specific adapter with values.
Please consider this post:
The matcher passed as argument to onData() must match the value as
returned by Adapter.getItem(). So the first version doesn't match,
because of the wrong type being used. It should be:
onData(is(instanceOf(IconRowAdapter.IconRow.class)))
What also can be a pitfall is using equalTo on different kinds of
CharSequences. String is a CharSequence, but if IconRow.getText()
returns CharSequence instead of String, then this can also be
Spannable, Editable, etc in which case equalTo wouldn't match. So if
IconRow.getText() return anything but String, make sure to convert it
into a String before comparison.
This post was taken from How to use Espresso to test item in adapter at a specific position
Your question lacks of code of tested class, so I cannot give you direct answer. I can only recommend to read StackOverflow link above.
Hope it help
You may need to "drill down" a little deeper into the view hierarchy to get to the item in the cell. Put an additional method call before the ".perform" using the id of the item in the grid cell
onChildView(withId(R.id.???)).perform(click());
Use the id of the view that the user would click on.

Testing ViewPager with multiple fragments using android espresso

I am trying to test my app which uses ViewPager. Each page contains fragments but these fragments are not always visible. I want to check visibility of a fragment in the currently visible page.
onView(withId(R.id.container_weather))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)));
But the problem is that espresso looks are all the pages not just the current page and I get the following error:
android.support.test.espresso.AmbiguousViewMatcherException: 'with id: eu.airpatrol.android:id/container_weather' matches multiple views in the hierarchy...
I had the same problem, however using the condition isCompletelyDisplayed() solved this problem as it only takes into account the on-screen views.
So, something like this should work:
onView(allOf(withId(R.id.container_weather), isCompletelyDisplayed()))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)));
Note: isDisplayed() works too in some cases but it also takes views off-screen into account and won't work if the ViewPager has any other page pr fragment loaded with the same view id.
Your tests are failing because of multiple elements with the same id. You can combine conditions using allOf(...). Then use isDisplayed() to check that matched view is visible on the screen. Below example can work:
onView(allOf(
withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE),
withId(R.id.container_weather)))
.check(matches(isDisplayed()));
Ran into this exact same problem. I was fortunate because the view hierarchies in my ViewPager can be easily identified by their siblings, so I was able to solve this using the hasSibling matcher, like so:
onView(
allOf(
hasSibling(withId(R.id.some_sibling)),
withId(R.id.field_to_test)
)
).perform(replaceText("123"));
Not a perfect solution as it can be slightly brittle, but in my case I think it was an acceptable compromise.
I had similar problem, where I was reusing the button layout and it was giving me a matches multiple views in the hierarchy exception.
So the easy work around I did was to create 2 different screens and have 2 different methods with different text.
Withdraw Screen:
public WithdrawScreen clickWithdraw() {
onView(allOf(withId(R.id.save_button), withText("Withdraw")))
.perform(click());
return this;
}
Deposit Screen:
public DepositScreen clickDeposit() {
onView(allOf(withId(R.id.save_button), withText("Deposit")))
.perform(click());
return this;
}
and in my tests, I create a new instance of both screens and call the above methods based on screen reference which is a bit easy to test for.
WithdrawScreen withdrawInstance = new WithdrawScreen();
withdrawInstance.clickWithdraw();
DepositScreen depositInstance = new DepositScreen();
depositInstance.clickDeposit();
The point was they were using same id - R.id.save_button for button and I was replacing text of button based on visibility of the fragment we are on.
Hope it helps.

UIAutomator click listview based on index

I'm trying to implement an UIAutomator testcase with a general method to perform a click on a ListView item (regardless of the type of viewgroup holding the listitem).
Currently I have following code, but it keeps on clicking the first item.
public void clickListViewItem(int index) throws UiObjectNotFoundException {
UiObject listview = new UiObject(new UiSelector().className("android.widget.ListView"));
if(index <= listview.getChildCount()){
listview.getChild(new UiSelector().index(index)).click();
}else{
throw new UIObjectNotFoundException("Index is greater than listSize");
}
}
I got it to work with following code, it is based on the clickable attribute of an UISelector:
listview.getChild(new UiSelector().clickable(true).index(index)).click();
The developer page implements a similar scenario, found here - although this assumes there is some identifying feature that exists in the child by which to select (like in example below, a string "Apps"):
If more than one matching element is found, the first matching element in the layout hierarchy is returned as the target UiObject. When constructing a UiSelector, you can chain together multiple properties to refine your search. If no matching UI element is found, a UiAutomatorObjectNotFoundException is thrown.
You can use the childSelector() method to nest multiple UiSelector instances. For example, the following code example shows how your test might specify a search to find the first ListView in the currently displayed UI, then search within that ListView to find a UI element with the text property Apps.
val appItem: UiObject = device.findObject(
UiSelector().className("android.widget.ListView")
.instance(0)
.childSelector(
UiSelector().text("Apps")
)
)

Categories

Resources