mockito-kotlin icon indicating copy to clipboard operation
mockito-kotlin copied to clipboard

mock<MyClass>() not working with coroutines

Open ninad458 opened this issue 3 years ago • 1 comments

    @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 avatar Jul 15 '21 12:07 ninad458

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

  • I could solve this with switching dispatcher is init block of CoroutineTestRule.
  • create viewmodel by lazy.

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

ninad458 avatar Jul 15 '21 12:07 ninad458

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.

TimvdLippe avatar Dec 18 '22 19:12 TimvdLippe