mercredi 26 décembre 2018

How to verify observed ViewModel LiveData inside my test?

I am currently trying to write a test for my ViewModel class. I already managed to write some tests for its LiveData updates, and verified behaviors.

However when I tried to test a method with chained methods, and see if my linked LiveData updated correctly, there is nothing happening. Test gives error stating that there is no invoke happened during test.

Below is how I tried to test the LiveData object

class WorkViewModelTest : AndroidTest() {
@Mock
private lateinit var sqlQueryUseCase: SqlQueryUseCase
private lateinit var workViewModel: WorkViewModel
val prefs = PreferencesHelper.defaultPrefs(context())
val resourceProvider = ResourceProvider(context())

@Before
fun setup() {
    workViewModel = WorkViewModel(sqlQueryUseCase, prefs, resourceProvider)
    workViewModel.toggleProductListState.observeForever {}
}

@Test
fun `When invalid barcode text given to function, it returns error message`() {
    //given
    val invalidString: String? = null
    val observer = mock<Observer<String>>()
    workViewModel.warningMessage.observeForever(observer)

    //when
    workViewModel.checkProductCode(invalidString)

    //then
    verify(observer).onChanged(resourceProvider.getStringFromResources(R.string.work_screen_empty_product_code_warning))
}

---> This is the test that fails

@Test
fun `When user read a barcode and it is not found on remote with ERP System, we should move our state to error`() {
    //given
    val barcode = "123456789"
    val observer = mock<Observer<String>>()
    workViewModel.warningMessage.observeForever(observer)

    given {
        runBlocking { sqlQueryUseCase.run(any()) }
    }.willReturn(Either.Right(emptyList())

    //when
    runBlocking { workViewModel.checkProductCode(barcode) }
    //then
    verify(observer).onChanged(resourceProvider.getStringFromResources(R.string.work_screen_data_not_found_for_given_barcode_warning))
}

Here is my ViewModel class, which invokes some use cases and fetch data from remote connection:

class WorkViewModel
@Inject constructor(
private val sqlQueryUseCase: SqlQueryUseCase,
private val prefs: SharedPreferences,
private val resourceProvider: ResourceProvider
) : BaseViewModel() {

val productListState: MutableLiveData<Boolean> = MutableLiveData()
val itemList: MutableLiveData<List<ItemDetailModel>> = MutableLiveData()
val warningMessage: MutableLiveData<String> = MutableLiveData()
val productInformation: MutableLiveData<String> = MutableLiveData()

val toggleProductListState = Transformations.map(itemList) {
    if (it.isEmpty()) {
        productListState.postValue(false)
    } else {
        productListState.postValue(true)
    }
}

fun initializeItemList(itemList: List<ItemDetailModel>) {
    this.itemList.postValue(itemList)
}

fun checkProductCode(productCode: String?) {
    if (productCode.isNullOrEmpty()) {
        warningMessage.postValue(resourceProvider.getStringFromResources(R.string.work_screen_empty_product_code_warning))
    } else {
        executeSqlQuery(productCode)
    }
}

private fun executeSqlQuery(productCode: String) {
    sqlQueryUseCase(
        SqlQueryUseCase.Params(
            String.createAuthToken(PreferencesHelper.getAuthorizationToken(prefs)),
            RequestBody.create(
                MediaType.parse("application/json"),
                QueryCreatorFactory.createQueryCreatorFromErpType(
                    prefs,
                    resourceProvider
                ).getProductInformationFromBarcodeQuery(productCode)!!.toByteArray(Charsets.UTF_8)
            )
        )
    ) {
        it.either(::handleErrorState, ::handleQueryResult)
    }
}

private fun handleQueryResult(resultData: List<Any>) {
    if (resultData.isNotEmpty()) {
        productInformation.postValue(resultData[0] as String)
    } else {
        warningMessage.postValue(resourceProvider.getStringFromResources(R.string.work_screen_data_not_found_for_given_barcode_warning))
    }
}

private fun handleErrorState(failure: Failure) {
    if (failure is Failure.ServerError)
        warningMessage.postValue(failure.errorMessage)
    else
        warningMessage.postValue("Some other failure")
}
}

What I am trying to achieve is, whenever user tries to get information about a product with a barcode, which actually does not stored in system, we will get an empty list as a response, and at that case, ViewModel should post a warning message via warningMessage LiveData.

However with the test I wrote, I cannot see in debug mod that "handleQueryResult" function is called at all. I am struggling with that problem, and since I am a bit new with Mockito, I cannot understand what is wrong with my test. Any help is welcomed. Thanks a lot.

Aucun commentaire:

Enregistrer un commentaire