6

I'm trying to make a simple espresso test that will find the first item in RecyclerView without specific label and click on it. To achieve this I added espresso-contrib to project like this:

androidTestCompile('com.android.support.test.espresso:espresso-contrib:2.2.2') {
    // Necessary to avoid version conflicts
    exclude group: 'com.android.support', module: 'appcompat'
    exclude group: 'com.android.support', module: 'design'
    exclude group: 'com.android.support', module: 'support-v4'
    exclude group: 'com.android.support', module: 'appcompat-v7'
    exclude group: 'com.android.support', module: 'support-annotations'
    exclude module: 'recyclerview-v7'
}

and wrote the following expression in my test case: onView(withId(R.id.sresults_list_recycler)).perform(RecyclerViewActions.actionOnHolderItem(new FirstNotSoldOutMatcher(), click()).atPosition(1));

Matcher works perfectly and RecyclerView got scrolled to the target item. But then I get error:

java.lang.NoSuchMethodError: No virtual method findViewHolderForPosition(I)Landroid/support/v7/widget/RecyclerView$ViewHolder; in class Landroid/support/v7/widget/RecyclerView; or its super classes (declaration of 'android.support.v7.widget.RecyclerView' appears in /data/app/com.example-1/base.apk)
at android.support.test.espresso.contrib.RecyclerViewActions$ActionOnItemAtPositionViewAction.perform(RecyclerViewActions.java:288)
at android.support.test.espresso.contrib.RecyclerViewActions$ActionOnItemViewAction.perform(RecyclerViewActions.java:232)
at android.support.test.espresso.ViewInteraction$1.run(ViewInteraction.java:144)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:428)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6077)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)

This looks strange to me. I read in Google documentation that findViewHolderForPosition method is deprecated now but it should still be there. I also have multidex enabled in the app but according to docs it is supported out of the box so shouldn't be a problem too.

Do you have any idea what can be wrong with this test?

tir38
  • 9,810
  • 10
  • 64
  • 107
Bersh
  • 2,789
  • 3
  • 33
  • 47

2 Answers2

8

Ok, in my case problem was in ProGuard shrinking unused methods. Disabling it for tests helped.

For future seekers - if you don't want to disable proguard for debug build adding this line to proguard config should help too:

-keepclasseswithmembers public class android.support.v7.widget.RecyclerView { *; }

Also, note that this rule should be added to the regular proguard file(the one of listed in proguardFiles) and not the test one(declared as testProguardFile)

Bersh
  • 2,789
  • 3
  • 33
  • 47
  • @Bersh why in `proguardFiles` and not in `testProguardFiles`? they should be just for tests... – Daniel Gomez Rico Feb 09 '18 at 20:11
  • @Caipivara as I remember `testProguardFiles` were only applicable for tests code(in test apk) while we need to keep methods in the app itself – Bersh Feb 12 '18 at 09:35
  • The weird thing is that I only get those crashes running my tests, when I use it manually it works fine without those rules – Daniel Gomez Rico Feb 12 '18 at 13:21
  • It's because then you use it manually in app/src, proguard sees it as dependency on that method and don't remove it, while if you use it in tests, proguard doesn't treat it as dependent code and removes the method – Nexen Jun 16 '19 at 03:28
  • For androidx lib: -keepclasseswithmembers public class androidx.recyclerview.widget.RecyclerView { *; } – Cüneyt Nov 15 '20 at 10:21
3

You can combine @Bersh's answer and this answer to be more strict about what to keep:

-keep class android.support.v7.widget.RecyclerView  {
    public android.support.v7.widget.RecyclerView$ViewHolder findViewHolderForPosition(int);
}

There is no reason to keep the entire RecyclerView class.

Update:

Or for when you inevitably update to AndroidX:

-keep class androidx.recyclerview.widget.RecyclerView  {
    public androidx.recyclerview.widget.RecyclerView$ViewHolder findViewHolderForPosition(int);
}
tir38
  • 9,810
  • 10
  • 64
  • 107