Android Espresso testing ProgressBar visibility GONE before assert

Viewed 1256

I'm trying to test a spinner that should display while loading the information from an API. The problem is that I can't assert the initial state VISIBLE because it disappear too fast when the results are emitted back thus always having a failing test

Expected: (view has effective visibility <VISIBLE> and view.getGlobalVisibleRect() to return non-empty rectangle)

Got: view.getVisibility() was <GONE>

The first attempt using ui-automator

@Test
    fun displayLoaderWhileFetchingPlaylistDetails() {
        IdlingRegistry.getInstance().unregister(idlingResource)
        uiObjectWithId(R.id.playlist_list).getChild(UiSelector().clickable(true).index(0)).click()
        val spinner = uiObjectWithId(R.id.playlist_details_loader)
        assertTrue(spinner.exists())
    }

Another variant for the test without ui-automator

@Test
    fun displayLoaderWhileFetchingPlaylistDetails2() {
        IdlingRegistry.getInstance().unregister(idlingResource)
        onView(
            allOf(
                withId(R.id.playlist_image),
                isDescendantOfA(withPositionInParent(R.id.playlist_list, 0))
            )
        )
            .perform(click())
        assertDisplayed(R.id.playlist_details_loader)
    }

ui-automator helper

fun uiObjectWithId(@IdRes id: Int): UiObject {
        val resourceId = getTargetContext().resources.getResourceName(id);
        val selector = UiSelector().resourceId(resourceId)
        return UiDevice.getInstance(getInstrumentation()).findObject(selector)
    }

Fragment

private fun observeLoaderState() {
        viewModel.playlistLoader.observe(this as LifecycleOwner) { playlistSpinner ->
            when (playlistSpinner) {
                true -> playlist_details_loader.visibility = View.VISIBLE
                else -> playlist_details_loader.visibility = View.GONE
            }
        }
    }

ViewModel

class PlaylistDetailViewModel(
    private val repository: PlaylistRepository
) : ViewModel() {

    val playlistLoader = MutableLiveData<Boolean>()

    fun getPlaylistDetails(playlistId: String) = liveData {
        playlistLoader.postValue(true)
        emitSource(
            repository.getPlaylistDetailsById(playlistId)
                .onEach { playlistLoader.postValue(false) }
                .asLiveData()
        )
    }
}

Thanks!

1 Answers

In Android when you set a View's visibility to GONE the renderer does not draw the View object, so the View practically has no dimensions. The same applies if you call any function that searchs in the UI tree for the View that has visibility set to GONE, and will return no match. If your only goal is to pass the test, my suggestions would be to set the View to INVISIBLE instead of GONE or to change the way you test for that specific layout.

From Android documentation:

  • View.GONE This view is invisible, and it doesn't take any space for layout purposes.
  • View.INVISIBLE This view is invisible, but it still takes up space for layout purposes.
Related