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()
this@getOrAwaitValueTest.removeObserver(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()
}
}
@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)
}