Я использую сопрограммы для выполнения асинхронного вызова на pull для обновления следующим образом:
class DataFragment : Fragment(), SwipeRefreshLayout.OnRefreshListener { // other functions here override fun onRefresh() { loadDataAsync() } private fun loadDataAsync() = async(UI) { swipeRefreshLayout?.isRefreshing = true progressLayout?.showContent() val data = async(CommonPool) { service?.getData() // suspending function }.await() when { data == null -> showError() data.isEmpty() -> progressLayout?.showEmpty(null, parentActivity?.getString(R.string.no_data), null) else -> { dataAdapter?.updateData(data) dataAdapter?.notifyDataSetChanged() progressLayout?.showContent() } } swipeRefreshLayout?.isRefreshing = false } }
Все здесь отлично работает, когда я на самом деле положил его на устройство. Моя ошибка, пустое состояние и состояние данных обрабатываются хорошо, а производительность – хорошая. Тем не менее, я также пытаюсь выполнить тестирование с помощью Spek. Тест My Spek выглядит следующим образом:
@RunWith(JUnitPlatform::class) class DataFragmentTest : Spek({ describe("The DataFragment") { var uut: DataFragment? = null beforeEachTest { uut = DataFragment() } // test other functions describe("when onRefresh") { beforeEachTest { uut?.swipeRefreshLayout = mock() uut?.onRefresh() } it("sets swipeRefreshLayout.isRefreshing to true") { verify(uut?.swipeRefreshLayout)?.isRefreshing = true // says no interaction with mock } } } }
Тест не работает, потому что он говорит, что не было взаимодействия с uut?.swipeRefreshLayout
mock. После некоторых экспериментов, похоже, это потому, что я использую контекст пользовательского интерфейса через async(UI)
. Если я сделаю это просто регулярным асинхронным, я могу пройти тест, но затем приложение выйдет из строя, потому что я изменяю виды за пределами потока пользовательского интерфейса.
Любые идеи, почему это может произойти? Кроме того, если у кого-то есть лучшие предложения для этого, что сделает его более проверяемым, я все уши.
Благодарю.
EDIT: Забыл упомянуть, что я также пытался обернуть verify
и uut?.onRefresh()
в runBlocking
, но я до сих пор не имел успеха.
Если вы хотите сделать вещи чистыми и подумать об использовании архитектуры MVP в будущем, вы должны понимать, что CourutineContext – это внешняя зависимость, которая должна быть введена через DI или передана вашему ведущему. Подробнее о теме .
Ответ на ваш вопрос прост, вы должны использовать только Unconfined CourutineContext для своих тестов. ( подробнее ) Чтобы упростить создание объекта, например Injection with:
package com.example object Injection { val uiContext : CourutineContext = UI val bgContext : CourutineContext = CommonPool }
и в тестовом пакете создать абсолютно тот же объект, но изменить на:
package com.example object Injection { val uiContext : CourutineContext = Unconfined val bgContext : CourutineContext = Unconfined }
и внутри вашего класса это будет что-то вроде:
val data = async(Injection.bgContext) {service?.getData()}.await()