polendina
09/26/2023, 7:40 PMViewModel
whereby it contains a function that calls another suspending function of a library, then assign its return value to an internal state. I want to uni test this function, but unit tests end before the inner suspending function returns.
class FloatingBubbleViewModel(
private val application: Application = Application(),
private val coroutineDispatcher: CoroutineDispatcher = Dispatchers.Default
) : AndroidViewModel(application) {
fun translateWord() =
viewModelScope.launch(coroutineDispatcher) { // Doesn't work
// runBlocking { // Works during testing
targetWordDisplay = Translator().translate(
text = srcWordDisplay,
source = Language.AUTO,
target = Language.ENGLISH
).translatedText
}
}
during unit testing, I've modified the dispatcher, but to no avail. It only works when the original code is invoked using runBlocking
rather than ViewModeScope
(even after injecting a custom dispatcher)
class TranslationTest {
@ExperimentalCoroutinesApi
@get:Rule
val mainCoroutineRule = MainCoroutineRule()
@OptIn(ExperimentalCoroutinesApi::class)
private val testDispatcher: TestDispatcher = UnconfinedTestDispatcher()
private lateinit var floatingBubbleViewModel: FloatingBubbleViewModel
@Before
fun setup() {
floatingBubbleViewModel = FloatingBubbleViewModel()
}
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun translateWordTest(): Unit = runTest {
val word = "eingaben"
floatingBubbleViewModel.srcWordDisplay = word
floatingBubbleViewModel.translateWord() // Doesn't work
// floatingBubbleViewModel.translateWord().join() // Works
advanceUntilIdle()
println(floatingBubbleViewModel.targetWordDisplay}) // Returns empty string.
}
}
@OptIn(ExperimentalCoroutinesApi::class)
class MainCoroutineRule(
private val dispatcher: TestDispatcher = StandardTestDispatcher()
): TestWatcher() {
override fun starting(description: Description?) {
super.starting(description)
Dispatchers.setMain(dispatcher)
}
override fun finished(description: Description?) {
super.finished(description)
Dispatchers.resetMain()
}
}