Как использовать TestCoroutineScope с хранилищем данных Android?

#android #kotlin #kotlin-coroutines #android-jetpack-datastore

#Android #kotlin #kotlin-сопрограммы #android-jetpack-хранилище данных

Вопрос:

У меня есть следующий код:

 class Feature(scope:CoroutineScope = CoroutineScope(Dispatchers.IO)){
 val dataStore: DataStore<MetaDataStore> = context.createDataStore(
        fileName = PREFERENCES,
        serializer = MetadataSerializer,
        scope = scope
    )
fun foo() {
}
}

 

При тестировании класса я хочу проверить функцию foo

 runBlockingTest {
            val feature = Feature(this)
             feature.foo()
             verifyStuff...

        }
 

Важно использовать TestCoroutineScope, поскольку это гарантирует мне, что любой асинхронный материал был завершен
К сожалению, я получаю сообщение об ошибке:

kotlinx.coroutines.test.Ошибка незавершенной сопрограммы: тест завершен с активными заданиями: [«сопрограмма #2»: ActorCoroutine{Active}@31b46ea7]

Это имеет смысл, поскольку хранилище данных может использовать фоновые задачи, но тогда как мне протестировать классы с помощью хранилища данных Android?

Я также задал вопрос в Google issue tracker: https://issuetracker.google.com/issues/177856517

Тем временем я поддержал некоторую фиктивную реализацию и внедрил ее в конструктор.

 import androidx.datastore.core.DataStore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock

class DatastoreInmemoryImpl<T>(defaultValue:T) : DataStore<T> {
    val sharedFlow = MutableStateFlow(defaultValue)
    val mutex = Mutex()
    override val data: Flow<T> = sharedFlow.asStateFlow()
    override suspend fun updateData(transform: suspend (t: T) -> T): T = mutex.withLock {
        sharedFlow.value = transform.invoke(sharedFlow.value)
        sharedFlow.value
    }
}
 

Но я должен иметь возможность использовать класс, использующий хранилище данных, без необходимости вводить его в качестве параметра конструктора

Ответ №1:

Ребята из Google ответили мне.

Здесь есть тестовый пример

Основная идея состоит в том, чтобы создать область, отличную от области тестирования, и впоследствии отменить ее

итак

 runBlockingTest {
  val dataStoreScope = TestCoroutineScope(TestCoroutineDispatcher()   Job())

  val feature = Feature(dataStoreScope)
  <put your test code here>
  dataStoreScope.cancel()
  dataStoreScope.cleanupTestCoroutines()
}
 

Создание хранилища данных и datastoreScope.cleanupTestCoroutines можно поместить в функции @Before и @After.

Они сказали мне, что работают над лучшим решением для более поздней версии

Комментарии:

1. ссылка мертва, извините.