4

I have this test that works about half the time.

  @Test
  public void thirdSwipe() {
    onView(withId(R.id.pager)).perform(swipeLeft());
    onView(withId(R.id.pager)).perform(swipeLeft());
    onView(withId(R.id.pager)).perform(swipeLeft());
    onView(allOf(withId(R.id.hint_english_text), withText("dog 0a"))).check(matches(isDisplayed()));
  }

I get this failure:

android.support.test.espresso.base.DefaultFailureHandler$AssertionFailedWithCauseError: 'is displayed on the screen to the user' doesn't match the selected view.
Expected: is displayed on the screen to the user
Got: "TextView{id=2131427358, res-name=hint_english_text, visibility=VISIBLE, width=624, height=62, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=20.0, y=20.0, text=dog 0a, input-type=0, ime-target=false, has-links=false}"

So, it looks like it found the TextView with "dog 0a", but it won't recognize it. I have looked at other questions and I do set the text with a String, this is just a few lines from my class:

private String  englishText;
englishTextView.setText(englishText);

Also, I am using allOf(). Any help would be appreciated. This view is inside of a ViewPager's view, so I'm not sure if the test is happening before the ViewPager is idyl, but it does say it finds the view.

Aleksandar G
  • 1,163
  • 2
  • 20
  • 25
flobacca
  • 936
  • 2
  • 17
  • 42
  • did you turn off animation? – Youngjae Apr 05 '16 at 02:04
  • yes I turned off animation. Sorry for not mentioning that. – flobacca Apr 05 '16 at 02:08
  • did you change device? for me, only lollipop device gives right result. – Youngjae Apr 05 '16 at 02:32
  • Maybe you should wait a little: http://stackoverflow.com/questions/21417954/espresso-thread-sleep – ozo Apr 05 '16 at 05:03
  • @Youngjae I've been using the same device for all tests. Android version 5.1. – flobacca Apr 06 '16 at 18:35
  • @ozo tried adding a sleep thread after the swipe and before the check and it's still flaky. Also added Flaky annotation and still didn't work. – flobacca Apr 06 '16 at 18:36
  • 1
    @flobacca // how about to apply `waitId` as this link suggests. It checks continuously until the value is acknowledged in given period. http://stackoverflow.com/questions/21417954/espresso-thread-sleep/22563297#22563297 – Youngjae Apr 07 '16 at 01:57
  • So my test class actually has a series of tests, firstSwipe(), secondSwipe()... They all look much like thirdSwipe() except firstSwipe() only has one swipeLeft() and tests for 'apple 0a', secondSwipe() has two swipeLeft()s tests 'banana 0a'. Okay, so I applied the waitId answer (the answer marked as correct) to thirdSwipe() and it made my firstSwipe() test fail instead sometimes. So I added the waitId to all tests and all tests passed for a while, but then firstSwipe() became flaky. So I copied firstSwipe into firstSwipeA() and firstSwipeB() and firstSwipeA() fails, but firstSwipeB() passes. – flobacca Apr 08 '16 at 20:44
  • I also tried Chiuki's example (found inside MattMatt's answer), but had the same problem where just another test became flaky. – flobacca Apr 08 '16 at 20:50

2 Answers2

6

You're definitely on the right track with the idling issue. Espresso has no way to know when a ViewPager has fully settled after a swipe is performed. As the failure implies, the view you're looking for is somewhere in the view hierarchy, which means that it's been rendered, but Espresso is detecting that it's not on screen yet. This is because the ViewPager hasn't fully settled onto the page you're interested in.

The next question would be how to tell Espresso to wait for the ViewPager to settle. This is a somewhat tough problem to solve.

A way to eliminate the likelihood of this problem is to perform a Thread.sleep, which will force Espresso to wait for some amount of time, with the hopes that the ViewPager settles before the wait is over. While this can generally work with a large enough sleep duration, this isn't ideal as this adds unnecessary time to your tests.

The best way to go about this would be to have ViewPager tell us when it's fully done settling.

If you take a look at some of the listeners available for ViewPager, one of them is the OnPageChangeListener. This class has a couple callback methods: onPageScrolled, onPageScrollStateChanged, and onPageSelected.

onPageSelected sounds like a good candidate, but if you take a closer look at the documentation for this method, you'll notice it says

/**
 * This method will be invoked when a new page becomes selected. Animation is not
 * necessarily complete.
 */

So this isn't going to work.

onPageScrollStateChanged is another good candidate. This reports a couple state changes in ViewPager, namely, SCROLL_STATE_DRAGGING, SCROLL_STATE_SETTLING, and SCROLL_STATE_IDLE. So using this callback, we can detect when the ViewPager goes idle.

As to how to hook an instance of this listener up to the ViewPager and Espresso, you can take a look at this implementation provided by Espresso for performing actions on DrawerLayouts and similarly waiting for them to settle. The jist of it is that you create a custom ViewAction to use that attaches one of these listeners on to the ViewPager, then performs the swipe and waits for the ViewPager to report that it went into SCROLL_STATE_IDLE.

Note that this implementation uses reflection to remove and wrap a DrawerListener from the DrawerLayout if one exists before attaching it's own, but this isn't necessary in the ViewPager case, as ViewPager can have multiple OnPageChangeListeners attached to it.

Jason Su
  • 76
  • 3
0

this worked for me

onView(allOf(
  withId(R.id.pagerimage),
  ChildOf(withId(R.id.viewpager), 0)
      )).check(matches(isDisplayed()))
        .perform(swipeLeft());
Hasaan Ali
  • 1,192
  • 16
  • 22
skn
  • 1