Когда вы используете launch
, вы создаете сопрограмму, которая будет выполнять асинхронно . Использование runBlocking
никак не повлияет на это.
Ваши тесты не пройдены, потому что вещи внутри ваших запусков произойдут , но еще не произошло.
Самый простой способ убедиться, что ваши запуски были выполнены до выполнения каких-либо утверждений, - это вызвать на них .join()
.
fun someLaunch() : Job = launch {
foo()
}
@Test
fun `test some launch`() = runBlocking {
someLaunch().join()
verify { foo() }
}
Вместо сохранения отдельных Jobs
в вашем ViewModel
, в onCleared()
вы можете реализовать свой CoroutineScope
следующим образом:
class MyViewModel : ViewModel(), CoroutineScope {
private val job = SupervisorJob()
override val coroutineContext : CoroutineContext
get() = job + Dispatchers.Main
override fun onCleared() {
super.onCleared()
job.cancel()
}
}
Все запуски, которые происходят в CoroutineScope
, становятся дочерними по отношению к этому CoroutineScope
, поэтому, если вы отмените job
(что фактически отменяет CoroutineScope
), то вы отмените все сопрограммы, выполняющиеся в этой области.
Итак, как только вы очистите реализацию CoroutineScope
, вы можете заставить свои ViewModel
функции просто возвращать Job
s:
fun loadSomeObjects1() = launch {
val objects1Result = repository.getSomeObjects1Async()
objects1.value = objects1Result
}
и теперь вы можете легко проверить их с помощью .join()
:
@Test
fun `loadObjects1 should get objects1`() = runBlocking {
viewModel.someObjects1.observeForever(observer)
val expectedResult = listOf(SomeObject("test1"))
`when`(repository.getSomeObjects1Async())
.thenReturn(expectedResult)
viewModel.loadSomeobjects1().join()
verify(observer).onChanged(listOf(SomeObject("test1")))
}
Я также заметил, что вы используете Dispatchers.Main
для ViewModel
. Это означает, что по умолчанию вы будете выполнять все сопрограммы в главном потоке. Вы должны подумать, действительно ли это то, что вы хотите сделать. В конце концов, в главном потоке нужно сделать очень мало вещей, не связанных с пользовательским интерфейсом, и ваша ViewModel не должна напрямую управлять пользовательским интерфейсом.