首页 > 解决方案 > 在 `runBlockingTest` 中测试 Room 的事务查询

问题描述

如何测试Room 的Transaction数据库操作?insertItineraries

错误

java.lang.IllegalStateException:此作业尚未完成

在 kotlinx.coroutines.JobSupport.getCompletionExceptionOrNull(JobSupport.kt:1188) 在 kotlinx.coroutines.test.TestBuildersKt.runBlockingTest(TestBuilders.kt:53) 在 kotlinx.coroutines.test.TestBuildersKt.runBlockingTest$default(TestBuilders.kt:45 ) 在 com.andigeeky.skyscannertest.db.ItineraryDaoTest.test 在 sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 在 sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 处插入行程 (ItineraryDaoTest.kt:44) )

行程道

@Dao
@OpenForTesting
interface ItineraryDao {

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertLegEntities(legs: List<LegEntity>)

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertItineraryEntities(itineraries: List<ItineraryEntity>)

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertItineraryLeg(itineraryLegEntities: List<ItineraryLegEntity>)

    @Transaction
    suspend fun insertItineraries(
        itineraries: List<ItineraryEntity>,
        legs: List<LegEntity>,
        itineraryLegEntities: List<ItineraryLegEntity>
    ){
        insertItineraryEntities(itineraries)
        insertLegEntities(legs)
        insertItineraryLeg(itineraryLegEntities)
    }

    @Transaction
    @Query("SELECT * FROM ItineraryEntity")
    fun getItineraryWithLegs(): LiveData<List<ItineraryWithLegs>>
}

行程道测试

@ExperimentalCoroutinesApi
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [Build.VERSION_CODES.P])
class ItineraryDaoTest : DbTest() {

    @get:Rule
    var instantTaskExecutorRule = InstantTaskExecutorRule()
    @Captor
    lateinit var captor: ArgumentCaptor<ArrayList<ItineraryWithLegs>>

    @Before
    fun setUp() {
        MockitoAnnotations.initMocks(this)
    }

    @Test
    fun `test insert itineraries with legs`() {
        runBlockingTest {
            val observer = mock<Observer<List<ItineraryWithLegs>>>()
            val legs = TestUtil.createLegs(1)
            val itineraries = TestUtil.createItineraries(1)
            val itineraryLegs = TestUtil.createItineraryLegEntities(1)

            skyScannerDatabase.itineraryDao().insertItineraries(itineraries, legs, itineraryLegs)
            skyScannerDatabase.itineraryDao().getItineraryWithLegs().observeForever(observer)

            captor.run {
                verify(observer, times(1)).onChanged(capture())
                assertEquals(itineraryLegs.size, value.size)
            }
        }
    }
}

数据库测试

@UseExperimental(ExperimentalCoroutinesApi::class)
abstract class DbTest : CoroutineTestBase() {
    @Rule
    @JvmField
    val countingTaskExecutorRule = CountingTaskExecutorRule()
    lateinit var skyScannerDatabase: SkyScannerDatabase

    @Before
    fun initDb() {
        val app = ApplicationProvider.getApplicationContext<Context>()
        skyScannerDatabase = Room.inMemoryDatabaseBuilder(app, SkyScannerDatabase::class.java)
            .allowMainThreadQueries()
            .setTransactionExecutor(Executors.newSingleThreadExecutor())
            .build()
    }

    @After
    fun closeDb() {
        countingTaskExecutorRule.drainTasks(10, TimeUnit.SECONDS)
        skyScannerDatabase.close()
    }
}

标签: androidunit-testingtestingandroid-roomkotlin-coroutines

解决方案


您应该在测试类的顶部使用@RunWith(AndroidJUnit4::class)注释来测试您的 DAO。作为仪器测试运行(测试 androidTest 文件夹下的类)。

@RunWith(AndroidJUnit4::class)
class YourTestClass {
  @get:Rule
  var instantTaskExecutorRule = InstantTaskExecutorRule()
  lateinit var skyScannerDatabase: SkyScannerDatabase

  @Before
  fun initDb() {
    val app = ApplicationProvider.getApplicationContext<Context>()
    skyScannerDatabase = Room.inMemoryDatabaseBuilder(app, SkyScannerDatabase::class.java)
        .allowMainThreadQueries()
        .setTransactionExecutor(Executors.newSingleThreadExecutor())
        .build()
  }

  @After
  fun closeDb() {
    skyScannerDatabase.close()
  }

  // ...

  @Test
  fun testMethod() = runBlockingTest {
    skyScannerDatabase.itineraryDao().insertItineraries(...)
  }
}

你的 build.gradle 模块应该有以下配置。

android {
  // ...

  defaultConfig {
    // ...
    testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    // ...
  }
}

// kotlin_coroutines_version = '1.3.3'
// test_arch_core_testing = '2.1.0'
// test_ext_junit_version = '1.1.1'
// test_runner_version = '1.2.0'

dependencies {
  androidTestImplementation "androidx.arch.core:core-testing:$test_arch_core_testing"   
  androidTestImplementation "androidx.test.ext:junit:$test_ext_junit_version"   
  androidTestImplementation "androidx.test:runner:$test_runner_version"  
  androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$kotlin_coroutines_version"
}

看看这篇文章


推荐阅读