mockito-kotlin
mockito-kotlin copied to clipboard
mock<MyClass>() not working with coroutines
@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)
)
}
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.
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.