Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit Testing LiveData android.os.Looper not mocked

Tags:

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?

like image 324
tyczj Avatar asked Apr 10 '19 15:04

tyczj


1 Answers

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() 
like image 64
Basel al natour Avatar answered Oct 20 '22 07:10

Basel al natour