mockito / mockito-kotlin

Using Mockito with Kotlin
MIT License
3.11k stars 202 forks source link

mock<MyClass>() not working with coroutines #437

Closed ninad458 closed 1 year ago

ninad458 commented 3 years ago
    @get:Rule
    val dispatcherRule = CoroutineTestRule()

    private val validator: Validator = mock()

    private val viewModel = MainViewModel(validator)

    @Test
    fun `test user data retrieved successfully`() = dispatcherRule.runBlockingTest {
        viewModel.getData()
    }

The above piece of code doesn't work. But the below one does.

    @get:Rule
    val dispatcherRule = CoroutineTestRule()

    private lateinit var viewModel: MainViewModel

    @Mock
    lateinit var validator: Validator

    @Before
    fun setUp() {
        MockitoAnnotations.openMocks(this)
        viewModel = MainViewModel(validator)
    }

    @Test
    fun `test user data retrieved successfully`() = dispatcherRule.runBlockingTest {
        viewModel.getData()
    }

I feel that the first one is much cleaner. Any reason why it's not working? I tried to find out why it's failing but no luck.

Some more information, which might be needed to debug further.

ViewModel class

fun getData() {
        viewModelScope.launch {
            uiState.value = "Loading"
            if (!validator.validate()) {
                uiState.value = "Error"
                return@launch
            }
            delay(1500)
            uiState.value = "Success"
        }
    }

    init {
        viewModelScope
    }

viewModelScope extension variable

public val ViewModel.viewModelScope: CoroutineScope
    get() {
        val scope: CoroutineScope? = this.getTag(JOB_KEY)
        if (scope != null) {
            return scope
        }
        return setTagIfAbsent(
            JOB_KEY,
            CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
        )
    }
ninad458 commented 3 years ago

Found the problem. I was trying to access the viewModelScope in the init block. While the rule was switching the thread in starting.

Both the solutions have the code smell. Would love to know if there is any better way to solve this.

TimvdLippe commented 1 year ago

Closing per the above comment, as I feel like this is more related to the specific use case rather than a general mockito-kotlin question.