0

I tried to test the ViewModel using Junit and Mockito, getting null pointer exception. It returns cannot invoke flow collector. I want to test the responses coming in MpinDataKey, check all parameters are there in MpinDataKey, the api call etc.

Getting following exception :

Exception in thread "Test worker @coroutine#2" java.lang.NullPointerException: Cannot invoke "kotlinx.coroutines.flow.Flow.collect(kotlinx.coroutines.flow.FlowCollector, kotlin.coroutines.Continuation)" LiveData value was never set. java.util.concurrent.TimeoutException: LiveData value was never set.atLiveDataUtilTestKt.getOrAwaitValueTest(LiveDataUtilTest.kt:38)

data class MpinKeyData( val token: String, val publicKey: String)
sealed class Result<T> { 
class Loading<T>: Result<T>()
data class Success<T>(val data: T): Result<T>()
data class Error<T>(val errorMessage: String): Result<T>() 
} 
class LoginViewModel @Inject constructor(private val loginRepository: LoginRepository): ViewModel() {

    private var _mpinKeyResponse = MutableLiveData<Result<MpinKeyData>>()
    val mpinKeyResponse: LiveData<Result<MpinKeyData>>
        get() = _mpinKeyResponse

    
    fun getMpinkey(context: Context) {
        viewModelScope.launch {
            loginRepository.getMpinKey(context).collect() {
                _mpinKeyResponse.postValue(it)
            }
        }
    }
}
class LoginDataSource @Inject constructor() {

suspend fun getMpinKey(context: Context): Result<MpinKeyData> { 
return try { 
val retrofit = RetrofitBuilder.getUnAuthRetrofit(context)
val loginService: LoginService by lazy { retrofit.create(LoginService::class.java) 
  } 
val response = loginService.getMpinKey() ResponseHelper.processResponse(response)
}catch (e: Exception) {
Result.Error(e.message ?: "Exception Found") 
   } 
 } 
}
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
fun <T> LiveData<T>.getOrAwaitValueTest(
    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(value: T) {
            data = value
            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
}
class LoginViewModelTest {

    @Mock
    private lateinit var loginRepository: LoginRepository

    @Mock
    private lateinit var loginDataSource: LoginDataSource

    private val testDispatcher = StandardTestDispatcher()

    @get:Rule
    val instantTaskExecutorRule = InstantTaskExecutorRule()

    @ExperimentalCoroutinesApi
    @get:Rule
    val mainDispatcherRule = MainDispatcherRule()

    private val context: Context = mock(Context::class.java)

    @Before
    fun initSetUp(){
        MockitoAnnotations.openMocks(this)
        Dispatchers.setMain(testDispatcher)
    }

    @ExperimentalCoroutinesApi
    @Test
    fun test_beginEnroll_Success() = runTest{
//        Mockito.`when`(loginDataSource.getMpinKey(context)).thenReturn(Result.Success<MpinKeyData>)
        val vmodel = LoginViewModel(loginRepository)
        vmodel.getMpinkey(context)
        testDispatcher.scheduler.advanceUntilIdle()
        val  result = vmodel.mpinKeyResponse.getOrAwaitValueTest()
        Assert.assertEquals(0,result)
    }

    @ExperimentalCoroutinesApi
    @Test
    fun test_beginEnroll_Failure() = runTest{
        Mockito.`when`(loginDataSource.getMpinKey(context)).thenReturn(Result.Error("Something went wrong"))
        val vmodel = LoginViewModel(loginRepository)
        vmodel.getMpinkey(context)
        testDispatcher.scheduler.advanceUntilIdle()
        val  result = vmodel.mpinKeyResponse.getOrAwaitValueTest()
        Assert.assertEquals(1,result is Result.Error)
    }


    @After
    fun tearDown() {
        Dispatchers.resetMain()
    }

}
0

1 Answer 1

0
@ExperimentalCoroutinesApi
  @Test
  fun test_beginEnroll_Success() = runTest{
          doReturn(flowOf(Result.Success(data = emptyList<MpinKeyData>()))).`when`(loginRepository).getMpinKey(context)
          loginRepository.getMpinKey(context).test{
              assertEquals(Result.Success(emptyList<List<MpinKeyData>>()), awaitItem())
              cancelAndIgnoreRemainingEvents()
          }
          verify(loginRepository).getMpinKey(context)
  }

  @ExperimentalCoroutinesApi
  @Test
  fun test_beginEnroll_Failure() = runTest{

      val errorMessage = "Something went wrong"

      doReturn(flowOf(Result.Error<MpinKeyData>(errorMessage))).`when`(loginRepository).getMpinKey(context)

      loginRepository.getMpinKey(context).test {
          assertEquals(
              Result.Error<MpinKeyData>(errorMessage),
              awaitItem()
          )
          cancelAndIgnoreRemainingEvents()
      }
      verify(loginRepository).getMpinKey(context)
  }
Sign up to request clarification or add additional context in comments.

1 Comment

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.