Hi everyone, I hope you all have a great day. I am...
# test
f
Hi everyone, I hope you all have a great day. I am from iOS world and just getting started with kotlin testing. I have a difficulty making a test passing. here is my class to be tested
Copy code
import dev.icerock.moko.mvvm.viewmodel.ViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach

class SignupOtpViewModel: ViewModel() {
    private val _isNextButtonEnabledFlow = MutableStateFlow(false)
    val isNextButtonEnabledFlow: StateFlow<Boolean>
        get() = _isNextButtonEnabledFlow

    val otpField = MutableStateFlow("")

    init {
        setupBinding()
    }

    // create setup binding for otpField. Set _isNextButtonEnabledFlow to true if otpField count is 6
    fun setupBinding() {
        otpField
            .map { it.length == 6 }
            .onEach { _isNextButtonEnabledFlow.value = it }
    }
}
and this is my test class:
Copy code
import kotlin.test.Test
import kotlin.test.assertEquals

class SignupOtpTests {
    @Test
    fun init_shouldDisableNextButton() {
        val sut = makeSut()
        assertEquals(sut.isNextButtonEnabledFlow.value, false)
    }

    @Test
    fun fillCompleteOtp_shouldEnableNextButton() {
        val sut = makeSut()
        sut.otpField.value = "123456"
        assertEquals(sut.isNextButtonEnabledFlow.value, true)
    }

    fun makeSut() : SignupOtpViewModel {
        val sut = SignupOtpViewModel()
        return sut
    }
}
fillCompleteOtp_shouldEnableNextButton is failing. How can I fix it?
d
You didn't call collect on the flow in setupBinding()... meaning it didn't start flowing at all. You need to use the viewModel's coroutine context to
launch { ... }
and in there call collect on that flow. The thing is how to replace that coroutine context in test code...
f
thank you, I can make the test passing now. Adding your points, I should configure the test dispatcher also in the test class. here is my updated code
Copy code
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.test.setMain
import kotlin.test.AfterTest
import kotlin.test.BeforeTest
import kotlin.test.Test
import kotlin.test.assertEquals
class SignupOtpTests {
    @BeforeTest
    fun setUp() {
        Dispatchers.setMain(UnconfinedTestDispatcher())
    }

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

    @Test
    fun init_shouldDisableNextButton() {
        val sut = makeSut()
        assertEquals(sut.isNextButtonEnabled.value, false)
    }

    @Test
    fun fillCompleteOtp_shouldEnableNextButton() = runTest {
        val sut = makeSut()
        sut.otpField.value = "123456"
        assertEquals(sut.isNextButtonEnabled.value, true)
    }

    fun makeSut() : SignupOtpViewModel {
        val sut = SignupOtpViewModel()
        return sut
    }
}
Copy code
import dev.icerock.moko.mvvm.viewmodel.ViewModel
import dev.icerock.moko.mvvm.flow.cMutableStateFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import dev.icerock.moko.mvvm.flow.cStateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch

class SignupOtpViewModel: ViewModel() {
    val _isNextButtonEnabled = MutableStateFlow(false)
    val isNextButtonEnabled: StateFlow<Boolean>
        get() = _isNextButtonEnabled.cStateFlow()

    val otpField = MutableStateFlow("").cMutableStateFlow()

    init {
        setupBinding()
    }

    fun setupBinding() {
        viewModelScope.launch {
            otpField
                .map { it.length == 6 }
                .collect { _isNextButtonEnabled.value = it }
        }
    }
}
👍🏼 1