android-test icon indicating copy to clipboard operation
android-test copied to clipboard

Espresso sometimes does not wait enough for RecyclerView with setHasFixedSize(true) to load

Open jongerrish opened this issue 6 years ago • 10 comments

Steps to reproduce:

  1. Open attached project
  2. Run ExampleInstrumentedTest couple of times, at least one test will fail eventually

If you add Thread.Sleep to the test or remove setHasFixedSize(true), tests will stop failing RecyclerTest.zip

Migrated from: https://issuetracker.google.com/123653014

jongerrish avatar Mar 04 '19 19:03 jongerrish

@jongerrish Reproduced this with your test project, but I noticed it is using the Support Library

Is this reproducible with AndroidX APIs as well?

(BTW, I enjoyed your Android Dev Summit Testing Rebooted talk a lot 😄 )

mrk-han avatar Mar 04 '19 21:03 mrk-han

(Author of the original ticket here)

Yes, this happens on AndroidX as well. The test project is just most basic reproducible project and Android Studio uses support library by default when creating new project.

matejdro avatar Mar 05 '19 06:03 matejdro

Are the APIs identical? If not, then I think probably best to use a test project using AndroidX, no? Maybe I'm missing something here.

mrk-han avatar Mar 05 '19 08:03 mrk-han

Yes, APIs are identical.

matejdro avatar Mar 05 '19 08:03 matejdro

I'm seeing this issue as well in an AndroidX project. I ported @matejdro's example source to AndroidX and can reproduce the problem.

RecyclerTest-AndroidX.zip

Note that this occurs with 1.1.1 (espresso 3.1.1) as well as the current (as of this writing) 1.2.0-beta-01 (espresso 3.2.0-beta01).

tsollas avatar May 23 '19 15:05 tsollas

For anyone looking for a workaround, I came up with this:

/**
 * Stop the test until RecyclerView's data gets loaded.
 *
 * Passed [recyclerProvider] will be activated in UI thread, allowing you to retrieve the View.
 *
 * Workaround for https://issuetracker.google.com/issues/123653014
 */
inline fun waitUntilLoaded(crossinline recyclerProvider: () -> RecyclerView) {
    Espresso.onIdle()

    lateinit var recycler: RecyclerView

    InstrumentationRegistry.getInstrumentation().runOnMainSync {
        recycler = recyclerProvider()
    }

    while (recycler.hasPendingAdapterUpdates()) {
        Thread.sleep(10)
    }
}

Then just call the method before verifying recycler stuff, passng recycler via lambda:

waitUntilLoaded { fragment.recycler }

matejdro avatar Jun 07 '19 07:06 matejdro

Is there any progress for this issue?

jonqchk avatar Jan 19 '21 06:01 jonqchk

Another workaround is to set the itemAnimator to null if animation are disabled:

        val animatorDurationScale = Settings.Global.getFloat(
            context.contentResolver, Settings.Global.ANIMATOR_DURATION_SCALE, 1F
        )
        recyclerView.itemAnimator = if (animatorDurationScale == 0F) {
            null
        } else {
            DefaultItemAnimator()
        }

sagix avatar Feb 25 '21 07:02 sagix

Any update on this?

MichalDanielDobrzanski avatar Sep 17 '21 08:09 MichalDanielDobrzanski

I believe this is still an issue. One option would be to define a RecyclerView idling resource but that requires significant changes to the source code just to support testing, something I'd rather not do.

Are folks using the workarounds in this thread?

vinaygopinath avatar Feb 15 '24 19:02 vinaygopinath