5.17
5. 测试您的数据库
之前的 Codelab 讨论了测试代码的重要性。在此任务中,您将添加一些单元测试来测试 DAO 查询,然后按照此 Codelab 的步骤继续操作时,还将添加更多测试。
如需测试数据库实现,推荐的方法是编写在 Android 设备上运行的 JUnit 测试。由于执行这些测试不需要创建 activity,因此它们的执行速度比界面测试速度快。
- 在
build.gradle.kts (Module :app)
文件中,请注意 Espresso 和 JUnit 的以下依赖项。
// Testing
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
- 切换到 Project 视图,然后右键点击 src > New > Directory,以便为测试创建测试源代码集。
- 从 New Directory 弹出式窗口中选择 androidTest/kotlin。
- 创建一个名为
ItemDaoTest.kt
的 Kotlin 类。 - 为
ItemDaoTest
类添加@RunWith(AndroidJUnit4::class)
注解。现在,您的类如以下示例代码所示:
package com.example.inventory
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class ItemDaoTest {
}
- 在类内,添加
ItemDao
和InventoryDatabase
类型的私有var
变量。
import com.example.inventory.data.InventoryDatabase
import com.example.inventory.data.ItemDao
private lateinit var itemDao: ItemDao
private lateinit var inventoryDatabase: InventoryDatabase
- 添加一个函数以创建数据库,并为其添加
@Before
注解,以便在每次测试之前能够运行该数据库。 - 在该方法内,初始化
itemDao
。
import android.content.Context
import androidx.room.Room
import androidx.test.core.app.ApplicationProvider
import org.junit.Before
@Before
fun createDb() {
val context: Context = ApplicationProvider.getApplicationContext()
// Using an in-memory database because the information stored here disappears when the
// process is killed.
inventoryDatabase = Room.inMemoryDatabaseBuilder(context, InventoryDatabase::class.java)
// Allowing main thread queries, just for testing.
.allowMainThreadQueries()
.build()
itemDao = inventoryDatabase.itemDao()
}
在此函数中,您将使用内存中的数据库,但不将该数据库保留在磁盘上。为此,您可以使用 inMemoryDatabaseBuilder() 函数。这样做的原因是,信息不需要保留,而是需要随着进程终止而被删除。您是在 .allowMainThreadQueries()
主线程中运行 DAO 查询,专门用于测试。
- 添加其他函数以关闭数据库。然后为该函数添加
@After
注解,以在每次测试后运行该函数来关闭数据库。
import org.junit.After
import java.io.IOException
@After
@Throws(IOException::class)
fun closeDb() {
inventoryDatabase.close()
}
- 在类
ItemDaoTest
中声明数据库要使用的商品,如以下代码示例所示:
import com.example.inventory.data.Item
private var item1 = Item(1, "Apples", 10.0, 20)
private var item2 = Item(2, "Bananas", 15.0, 97)
- 添加实用函数,先向数据库中添加一个商品,然后添加两个商品。稍后,您将在测试中使用这些函数。将它们标记为
suspend
,以便它们可以在协程中运行。
private suspend fun addOneItemToDb() {
itemDao.insert(item1)
}
private suspend fun addTwoItemsToDb() {
itemDao.insert(item1)
itemDao.insert(item2)
}
- 为向数据库中插入单个商品的
insert()
编写测试。将测试命名为daoInsert_insertsItemIntoDB
,并为其添加@Test
注解。
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.Test
@Test
@Throws(Exception::class)
fun daoInsert_insertsItemIntoDB() = runBlocking {
addOneItemToDb()
val allItems = itemDao.getAllItems().first()
assertEquals(allItems[0], item1)
}
在此测试中,您将使用实用函数 addOneItemToDb()
向数据库中添加一个商品。然后,读取数据库中的第一个商品。通过 assertEquals()
,您可以将预期值与实际值进行比较。您将使用 runBlocking{}
在新的协程中运行测试。此设置是将实用函数标记为 suspend
的原因。
- 运行测试并确保测试通过。
- 为数据库中的
getAllItems()
编写另一个测试。将测试命名为daoGetAllItems_returnsAllItemsFromDB
。
@Test
@Throws(Exception::class)
fun daoGetAllItems_returnsAllItemsFromDB() = runBlocking {
addTwoItemsToDb()
val allItems = itemDao.getAllItems().first()
assertEquals(allItems[0], item1)
assertEquals(allItems[1], item2)
}
在上述测试中,您在协程内向数据库中添加了两个商品。然后,您读取了这两个商品,并将其与预期值进行比较。