Testing Livedata With Robolectric
I have following test that checks if activity correctly gets data from repository through viewmodel.
@Config(application = TestApplication::class)
@RunWith(RobolectricTestRunner::class)
@LooperMode(LooperMode.Mode.PAUSED)
class BusinessTests {
private lateinit var viewModel: BusinessCollectionViewModel
private lateinit var activity: BusinessCollectionVerticalActivity
private lateinit var observer: Observer<Triple<NetworkState, PagedList<Edge<Business>>, TimeTracking?>>
@Before
fun setUp() {
observer = mock()
}
@Test
fun givenBusinessMock_whenVerticalCollection_thenBusinessVerticalWith2Items() {
val activityScenario = ActivityScenario.launch(BusinessCollectionVerticalActivity::class.java)
activityScenario.onActivity {
activity = it
}
viewModel = ViewModelProviders.of(activity)[BusinessCollectionViewModel::class.java]
viewModel.data.observeForever(observer)
assert(viewModel.data.value?.second?.size == 2)
}
}
Problem is that the test always fails, but in debug it passes correctly, but when I debug it with false condition in assert, following exception pops up.
java.lang.Exception: Main looper has queued unexecuted runnables. This might be the cause of the test failure. You might need a shadowOf(getMainLooper()).idle() call.
It's really strange behaviour and I don't know what to do. And of course I tried to add shadowOf(getMainLooper()).idle() before observe.
I'm using latest robolectric 4.3, could it be a bug?
Answer
The problem is that the thread that runs the UI test is finishing without any errors while the assert
is running in a different thread.
You can change your test method to hold the thread until it's release by the thread that runs the observeOnce:
@Test
fun givenBusinessMock_whenVerticalCollection_thenBusinessVerticalWith2Items() {
val syncObject = Object()
val activityScenario = ActivityScenario.launch(BusinessCollectionVerticalActivity::class.java)
activityScenario.onActivity {
activity = it
}
viewModel = ViewModelProviders.of(activity)[BusinessCollectionViewModel::class.java]
viewModel.data.observeOnce {
assert(it.second.size == 2)
synchronized (syncObject) {
syncObject.notify()
}
}
synchronized (syncObject) {
syncObject.wait()
}
}
If you want a better approach, you can observeForever in your data and check the value:
var observer: Observer<?> // replace ? by your type
viewModel.data.observeForever(observer)
assert(viewModel.data.value.second.size == 2)
More info in this article.
Related Questions
- → should I choose reactjs+f7 or f7+vue.js?
- → Phonegap Android write to sd card
- → Local reference jquery script in nanohttpd (Android)
- → Click to navigate on mobile devices
- → How to allow api access to android or ios app only(laravel)?
- → Access the Camera and CameraRoll on Android using React Native?
- → React native change listening port
- → What is the default unit of style in React Native?
- → Google play market autocomplete icon
- → Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of `ListView`
- → Using Laravel with Genymotion
- → react native using like web-based ajax function
- → react native pdf View