I am trying to unit test my ViewModel that posts some stuff back to the activity via LiveData
but when I run the method that sets the livedata value I get the error
java.lang.RuntimeException: Method getMainLooper in android.os.Looper not mocked.
I have read several posts and articles that all you have to do is add a Rule
for InstantTaskExecutorRule
and it should just work but I did that and I still get the error.
This is my unit test
val observer: Observer<String> = mock(Observer::class.java) as Observer<String>
@get:Rule
var rule: TestRule = InstantTaskExecutorRule()
@Test
fun testSearchDataValidationFailureMissingSearchLocation() {
val viewModel = MoveViewModel()
val param1 = 0
val param2 = 1
val param3 = "1234"
viewModel.dialogMessageLiveData.observeForever(observer)
Assert.assertFalse(viewModel.validateSearchData(param1, param2, param3))
verify(observer).onChanged("Data not valid")
}
This is the method I am trying to test
fun validateSearchData(param1: Int, param2: Int, param3: String): Boolean {
var valid: Boolean = false
if (param1 == 0 || param2 == 0 || param3.isBlank()) {
dialogMessageLiveData.postValue("Data not valid")
} else {
valid = true
}
return valid
}
I am not sure what else to do to fix this, can anyone suggest another solution?
you can use this extention to get the value of a [LiveData] or waits for it to have one, with a timeout. Use this extension from host-side (JVM) tests. It's recommended to use it alongside InstantTaskExecutorRule
or a similar mechanism to execute tasks synchronously.
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
fun <T> LiveData<T>.getOrAwaitValue(
time: Long = 2,
timeUnit: TimeUnit = TimeUnit.SECONDS,
afterObserve: () -> Unit = {}
): T {
var data: T? = null
val latch = CountDownLatch(1)
val observer = object : Observer<T> {
override fun onChanged(o: T?) {
data = o
latch.countDown()
[email protected](this)
}
}
this.observeForever(observer)
try {
afterObserve.invoke()
// Don't wait indefinitely if the LiveData is not set.
if (!latch.await(time, timeUnit)) {
throw TimeoutException("LiveData value was never set.")
}
} finally {
this.removeObserver(observer)
}
@Suppress("UNCHECKED_CAST")
return data as T
}
then in your test case you can get the value
viewModel.dialogMessageLiveData.getOrAwaitValue()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With