Jetpack Room 测试(上篇)

153 阅读3分钟

这是我参与11月更文挑战的第5天,活动详情查看:2021最后一次更文挑战

经过上一篇文章的练习,相信你已经对测试驱动开发(TDD)有了一个初步的认识。在移动开发中,数据库是个很特立独行的模块,它是和界面开发是可以分割开的,所以当我们边测试边开发APP的数据库模块时,完全可以不需要考虑先实现UI。

Room

本文是讲Room测试的,那理所应当先了解一下Room。

我理解的Room是一个Sqlite的工具库,它不仅和其它的Sqlite库一样有着使用起来简单方便的API,它还有编译器纠错功能,能和协程配合写出更清晰的异步代码。

对于Room,你需要先对关系型数据库Sqlite有些了解,不了解的可以点击这里:菜鸟Sqlite教程。Room其他的使用细节,都会在后面介绍。

起步

在测试之前我们需要准备好测试环境和数据库模块相关的资源。

本文选择一个歌曲播放APP的数据库模块来实现。歌曲与播放列表是一个经典的多对多关系,一首歌曲可能存在于多个播放列表中,一个播放列表中也可能有多个歌曲,在关系数据库的设计中一般需要要另一个表来表示他们的关系。接下来,我将一步步地介绍TDD的方式去构建这个数据库模块。

安装依赖

经过实验,我去掉了很多不需要的依赖。打开一个新的项目,你只需要添加以下依赖:

dependencies {    
    androidTestImplementation 'androidx.test:runner:1.3.0'
    androidTestImplementation 'androidx.test:rules:1.3.0'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
​
    def room_version = "2.3.0"
    implementation "androidx.room:room-runtime:$room_version"
    kapt "androidx.room:room-compiler:$room_version"
    implementation "androidx.room:room-ktx:$room_version"
}

这里有些需要注意的地方:最好使用较新的AS:若是你的Room有些莫名其妙的的报错,也请把Room更新到最新的稳定版。

创建表

前面我已经说了我们需要三张表:歌曲表、播放列表表和歌曲和播放列表关系表。

@Entity
data class Song(
    @PrimaryKey(autoGenerate = true) val songId: Int = 0,
    val songName: String,
    val singer: String
)
​
@Entity
data class PlayList(
    @PrimaryKey(autoGenerate = true) val playlistId: Int = 0,
    val playlistName: String,
    val desc: String
)
​
@Entity(primaryKeys = ["playlistId", "songId"])
data class PlaylistAndSong(
    val playlistId: Int,
    val songId: Int
)

@Entity 表示该类为数据类。@PrimaryKey 表示该属性为主键,你也可以像PlaylistAndSong类那样给几个属性都当作主键。

@PrimaryKey(autoGenerate = true) 需要说一下,在没有额外需求的情况下我们一般把主键设置为自增的,这样你的主键属性是不需要赋值,可是Kotlin语法又不允许,所以就给了这个主键一个默认值。

然后创建一个数据库类和数据库操作类(Dao)。

TestDataBase.kt

@Database(
    entities = [Song::class,PlayList::class,PlaylistAndSong::class],
    version = 1
)
abstract class TestDataBase: RoomDatabase() {
    abstract fun getTestDao():TestDao
}

TestDao.kt

@Dao
interface TestDao {
    // 此次编写数据库操作
}

这样所有的资源就准备好了。

测试

在 androidTest 文件夹中建立一个测试文件 「RoomTest.kt」,代码如下:

class RoomTest {
    private lateinit var dao: TestDao
    private lateinit var db: TestDataBase
​
    @Before
    fun createDb() {
        val context = ApplicationProvider.getApplicationContext<Context>()
        db = Room.inMemoryDatabaseBuilder(
            context, TestDataBase::class.java
        ).build()
        dao = db.getTestDao()
    }
​
    @After
    @Throws(IOException::class)
    fun closeDb() {
        db.close()
    }
}

在实际使用中我们也是只需要初始化一个bd实例,再获取Dao层对象就可以使用进行数据库操作了,最后可以再关闭db。@Before 表示在测试函数执行之前可以做的事,@After 表示在测试函数执行之后可以做的事。

万事俱备,只欠东风。下篇文章,就开始牛刀小试了。