Я изо всех сил пытаюсь протестировать своего ведущего, который вызывает приостановленную функцию из уровня репозитория следующим образом:
override fun viewCreated() {
launch {
val hasPermission = permissionChecker.execute() //suspended function
if (hasPermission) {
foo()
} else {
view.bar()
}
}
Ведущий также расширяет этот интерфейс:
interface CoroutinePresenter: CoroutineScope {
val job: Job
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + job
fun stopAllActiveJobs() {
coroutineContext.cancelChildren()
}
И приостановленная функция определяется следующим образом:
suspend fun execute() : Boolean = withContext(Dispatchers.IO) {
return@withContext class.foo()
}
В приложении все работает, как и ожидалось, но когда я попытался написать модульный тест, я заметил, что всякий раз, когда я вызываю фрагмент кода внутри запуск, поток переключается, но тест не ожидает выполнения. Это реализация теста:
@Test
fun `Test of Suspended Function`() = runBlocking {
presenter.viewCreated()
then(view).should().bar()
...
}
Я также добавил предложенную библиотеку для тестирования kotlinx-coroutines-test, но результат с ней все тот же. Я также пытался следовать предложению это, а также реализовать что-то вроде это, но все равно не повезло.
Я думаю, что проблема заключается в фактическом создании другого потока всякий раз, когда запуск вызывается в презентере, и тест на самом деле не знает, как его дождаться. Я также пытался вернуть Работа и вызвать job.join(), но не получилось с NullPointerException.
Надеюсь, вы, ребята, можете мне помочь.
Я нашел решение для этого: следуя учебнику это, я настроил оба
@Before
fun setup() {
Dispatchers.setMain(Dispatchers.Unconfined)
...
}
@After
fun tearDown() {
Dispatchers.resetMain() // reset main dispatcher to the original Main dispatcher
}
И запустив весь блок запуск класса presenter внутри оператора runBlocking в тесте. Проблема была также связана с необъявленным исключением внутри приостановленной функции, которое на самом деле не было высмеивали, но было невидимым для моих глаз.
Теперь все работает нормально.
Во-первых, я настоятельно рекомендую указать ваш coroutineContext в качестве параметра следующим образом:
class CoroutinePresenter(coroutineContext: CoroutineContext): CoroutineScope { init{ _coroutineContext = coroutineContext } override val coroutineContext: CoroutineContext get() = _coroutineContext // Your Methods }
В вашей реальной среде:
@YourScope @Provides fun providesCoroutinePresenter(coroutineContext:CoroutineContext ){ return CoroutinePresenter() } @YourScope @Provides fun providesCoroutineContext(){ return Dispatchers.Main + job }
Во время модульного теста:
@Before fun setUp() { coroutinePresenter CoroutinePresenter(Dispatchers.Unconfined) } @Test fun `Should do something`(){ //WHEN coroutinePresenter.doSomething(params) //THEN do your assertions }
Для получения дополнительной информации проверьте ТВЕРДЫЕ принципы и в этом случае Д.
Это очень хорошее решение, но я думаю, что использование библиотеки, предоставленной официальной командой kotlin, на самом деле лучше, поскольку вы собираетесь ввести дополнительный параметр в класс докладчика только для тестирования. Это, конечно, работает, как ожидалось.