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!
First you need to create a rule using ActivityScenarioRule.
in example:
@get:Rule
val mActivityRule = ActivityScenarioRule(MainActivity::class.java)
ActivityScenarioRule, which is a JUnit rule used for functional testing of Android activities. The purpose of this rule is to launch the specified activity (MainActivity in this case) before each test method runs and handle its lifecycle during the test.
Then you can write the test code for the display as below:
@Test
fun displaysLoaderWhileFetchingThePlaylist() {
mActivityRule.scenario.onActivity {
assertDisplayed(R.id.pbLoader)
}
}
The mActivityRule.scenario property returns an instance of ActivityScenario, which represents the current state of the activity under test. The ActivityScenario provides various methods to interact with the activity, such as launching, finishing, or monitoring the state of the activity.
onActivity executes a specific action in the main thread of the current Activity.
As a result, mActivityRule.scenario.onActivity is used to safely perform UI interactions in test code and ensure that these interactions are executed in the main thread.
I hope this answer will solve your problem.