Сначала вам нужно каким-то образом сделать вашу область сопрограммы инъекционной, либо создав для нее провайдер вручную, либо используя структуру для инъекций, такую как dagger. Таким образом, когда вы тестируете свою ViewModel, вы можете переопределить область сопрограммы с помощью тестовой версии.
Для этого есть несколько вариантов, вы можете просто сделать саму ViewModel инъекционной (статья об этом здесь: https://medium.com/chili-labs/android-viewmodel-injection-with-dagger-f0061d3402ff)
Или вы можете вручную создать поставщика ViewModel и использовать его, где бы он ни создавался. Несмотря ни на что, я настоятельно рекомендую некоторую форму внедрения зависимостей для достижения реальной тестируемости.
Тем не менее, ваша ViewModel должна иметь свой CoroutineScope , предоставленный , а не создавать экземпляр самой области сопрограммы.
Другими словами, вы можете захотеть
class MovieListViewModel(val couroutineScope: YourCoroutineScope) : ViewModel() {}
или, возможно,
class MovieListViewModel @Inject constructor(val coroutineScope: YourCoroutineScope) : ViewModel() {}
Независимо от того, что вы делаете для инъекции, следующим шагом будет создание вашего собственного интерфейса CoroutineScope. которые вы можете переопределить в тестовом контексте. Например:
interface YourCoroutineScope : CoroutineScope {
fun launch(block: suspend CoroutineScope.() -> Unit): Job
}
Таким образом, когда вы используете область видимости для своего приложения, вы можете использовать одну область, скажем, область сопрограммы жизненного цикла:
class LifecycleManagedCoroutineScope(
private val lifecycleCoroutineScope: LifecycleCoroutineScope,
override val coroutineContext: CoroutineContext = lifecycleCoroutineScope.coroutineContext) : YourCoroutineScope {
override fun launch(block: suspend CoroutineScope.() -> Unit): Job = lifecycleCoroutineScope.launchWhenStarted(block)
}
И для вашего теста вы может использовать тестовую область:
class TestScope(override val coroutineContext: CoroutineContext) : YourCoroutineScope {
val scope = TestCoroutineScope(coroutineContext)
override fun launch(block: suspend CoroutineScope.() -> Unit): Job {
return scope.launch {
block.invoke(this)
}
}
}
Теперь, поскольку ваша ViewModel использует область видимости типа YourCoroutineScope, и поскольку в приведенных выше примерах и жизненный цикл, и тестовая версия реализуют интерфейс YourCoroutineScope, вы можете использовать разные версии прицела в разных ситуациях, например приложение или тест.