Методы тестирования Room DAO с помощью Kotlin Coroutines и Flow
Я пытаюсь перейти с LiveData на Flow в моем Room Dao. Приложение работает нормально, но у меня проблемы с поведением при тестировании. Когда я запускаю тест, он запускается и работает бесконечно. Я также пытался использовать kotlinx.coroutines.test runBlockingTest, но у меня возникла проблема с сообщением "Это задание еще не завершено", как здесь. Может ли кто-нибудь указать мне в правильном направлении, как проверить поведение моего CoresDao?
@Dao
interface CoresDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertCores(cores: List<Core>)
@Transaction
suspend fun replaceCoresData(cores: List<Core>) {
deleteAllCores()
insertCores(cores)
}
@Query("SELECT * FROM cores_table")
fun getAllCores(): Flow<List<Core>>
@Query("DELETE FROM cores_table")
suspend fun deleteAllCores()
}
@RunWith(AndroidJUnit4::class)
class CoresDaoTest {
private lateinit var database: SpaceDatabase
private lateinit var coresDao: CoresDao
private val testDispatcher = TestCoroutineDispatcher()
private val testCoresList = listOf(core2, core3, core1)
@get:Rule
var instantTaskExecutorRule = InstantTaskExecutorRule()
@Before
fun setup() {
Dispatchers.setMain(testDispatcher)
val context = InstrumentationRegistry.getInstrumentation().targetContext
database = Room.inMemoryDatabaseBuilder(context, SpaceDatabase::class.java).build()
coresDao = database.coresDao()
}
@After
fun cleanup() {
database.close()
Dispatchers.resetMain()
testDispatcher.cleanupTestCoroutines()
}
@Test
fun testGetAllCores(): Unit = runBlocking {
withContext(Dispatchers.Main) {
runBlocking { coresDao.insertCores(testCoresList) }
val coresList = mutableListOf<Core>()
coresDao.getAllCores().collect { cores -> coresList.addAll(cores) }
assertThat(coresList.size, equalTo(testCoresList.size))
}
}
}
3 ответа
Для тестирования Flow я нашел лучшие API: .take(n).toList()
. Вы можете использоватьrunBlockingTest
и вам не нужно использовать withContext
чтобы переместить выполнение в другой поток.
Вы можете найти пример того, как это работает здесь:https://github.com/manuelvicnt/MathCoroutinesFlow/blob/master/app/src/test/java/com/manuelvicnt/coroutinesflow/fibonacci/impl/NeverEndingFibonacciProducerTest.kt
Поскольку вы используете TestCoroutineDispatcher
уже, используя runBlockingTest
ничего не будет делать в вашем примере.. Вам придется cancel
в Flow
после сбора или scope
в котором вы запустили Flow
изменить: пример такого правила можно найти здесь
Оказалось, что я неправильно обработал сбор и отмену Flow, и это, вероятно, было причиной проблемы. Ниже приведен код, который работает. Более сложный пример можно найти здесь.
@RunWith(AndroidJUnit4::class)
class CoresDaoTest {
private lateinit var database: SpaceDatabase
private lateinit var coresDao: CoresDao
private val testDispatcher = TestCoroutineDispatcher()
private val testCoresList = listOf(core2, core3, core1)
@get:Rule
var instantTaskExecutorRule = InstantTaskExecutorRule()
@Before
fun setup() {
Dispatchers.setMain(testDispatcher)
val context = InstrumentationRegistry.getInstrumentation().targetContext
database = Room
.inMemoryDatabaseBuilder(context, SpaceDatabase::class.java)
.setTransactionExecutor(Executors.newSingleThreadExecutor())
.build()
coresDao = database.coresDao()
}
@After
fun cleanup() {
database.close()
Dispatchers.resetMain()
testDispatcher.cleanupTestCoroutines()
}
@Test
fun testInsertAndGetAllCores() = runBlocking {
coresDao.insertCores(testCoresList)
val latch = CountDownLatch(1)
val job = launch(Dispatchers.IO) {
coresDao.getAllCores().collect { cores ->
assertThat(cores.size, equalTo(testCoresList.size))
latch.countDown()
}
}
latch.await()
job.cancel()
}