16

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...

JJD
  • 50,076
  • 60
  • 203
  • 339
Ubaier Bhat
  • 854
  • 13
  • 33

4 Answers4

10

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.

skbrhmn
  • 1,124
  • 1
  • 14
  • 36
6

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()));
JJD
  • 50,076
  • 60
  • 203
  • 339
denys
  • 6,834
  • 3
  • 37
  • 36
  • 1
    Thank you for your reply, I tried your solution but it still has the same problem. I have two pages and both contain this fragment and for some reason both have visibility=VISIBLE in the View Hierarchy. – Ubaier Bhat Aug 11 '15 at 14:44
  • Do you have some unique text or child element inside R.id.container_weather? If yes you can add this check also inside `allOf()`. – denys Aug 12 '15 at 10:13
  • 3
    The only difference between the two is the y-coordinate because the two are on different pages. :( – Ubaier Bhat Aug 13 '15 at 08:10
  • Did you find a solution for this ? I am stuck with the same issue. In my case both the fragments are exactly identical with only then data shown by them being the only difference – Dibzmania Jan 19 '17 at 04:26
  • Using `allOf()` fixed the problem for me (thanks) but note that most likely you'll want to be matching on `isDisplayed()` or `isCompletelyDisplayed()` and not `withEffectiveVisibility()` because the latter method matches on the `visibility` property of the `View` and not on whether it's actually visible on the screen. – Adil Hussain Apr 25 '20 at 09:01
3

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.

stork
  • 420
  • 1
  • 3
  • 12
0

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.

  1. Withdraw Screen:

    public WithdrawScreen clickWithdraw() {
        onView(allOf(withId(R.id.save_button), withText("Withdraw")))
            .perform(click());
        return this; 
    }
    
  2. 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.

JJD
  • 50,076
  • 60
  • 203
  • 339
Hiten Bahri
  • 259
  • 3
  • 9