在之前的一篇文章中,我们介绍了一下构件组件,其中就提到了使用 Room 来进行数据库操作。但我们也有注意到,如果您错误地实施迁移,最严重的结果可能会导致您的应用崩溃或丢失用户数据。
除此之外,Migration.migrate 在编译时不会检查在方法中执行的 SQL 语句,这导致了更多的问题。了解到这个情况之后,对迁移进行测试就成了一项必做任务。 Room 提供了 MigrationTestHelper 测试工具,这一工具允许您:
- 在给定的版本中创建一个数据库
- 在数据库上运行一组给定的迁移
- 验证数据库 schema
当然,Room 不会验证数据库中的数据本身。这是您需要亲自去实现的东西。
以下是您需要了解的有关 Room 迁移测试的内容。
工作原理
为了进行迁移测试,Room 需要知道您当前数据库版本的几个要素:版本号、Entity、Identity Hash,以及为创建和更新 room_master_table 而做出的查询请求。所有这些都由 Room 在编译时自动生成,并存储在schema JSON 文件中。
在 build.gradle 文件中指定一个文件夹,来放置这些生成的 schema JSON 文件。在更新 schema 时,最终会出现一些 JSON 文件,每个版本都将有一个对应的文件。确保将每个生成的文件提交给源代码管理工具。下次再次增加版本号码时,Room 可以使用 JSON 文件进行测试。
先决条件
要生成 JSON 文件,请使用以下内容更新 build.gradle 文件:
1. 定义 schema 位置
- defaultConfig {
- javaCompileOptions {
- annotationProcessorOptions {
- arguments = ["room.schemaLocation":
- "$projectDir/schemas".toString()]
- }
- }
- }
2. 将 schema 位置添加到源码目录
- android {
- sourceSets {
- androidTest.assets.srcDirs +=
- files("$projectDir/schemas".toString())
- }
3. 将 Room 测试库添加到依赖列表中
- dependencies {
- androidTestImplementation
- “android.arch.persistence.room:testing:1.0.0-alpha5”
- }
迁移测试规则
创建数据库和 schema,打开和关闭数据库,运行迁移 —— 您几乎需要为每个测试编写大量这样的样板代码。为了避免过度重复劳动,请在迁移测试类中使用 MigrationTestHelper 测试工具。
为了创建数据库以及验证迁移,MigrationTestHelper 很大程度上依赖于生成的 JSON 文件。
- @Rule
- public MigrationTestHelper testHelper =
- new MigrationTestHelper(
- InstrumentationRegistry.getInstrumentation(),
- <your_database_class>.class.getCanonicalName(),
- new FrameworkSQLiteOpenHelperFactory()
您可以在特定版本中创建数据库:
- // Create the database with version 2
- SupportSQLiteDatabase db =
- testHelper.createDatabase(TEST
您可以运行一组迁移,并自动验证 schema 是否更新无误:
- db = testHelper.runMigrationsAndValidate(TEST_DB_NAME, 4, validateDroppe
实施测试
测试策略很简单:
- 在特定版本中打开数据库;
- 插入一些数据;
- 运行迁移并验证 schema;
- 检查数据库中是否有正确的数据。
例如,数据库的版本 3 添加了一个新列:date 。因此,当测试从版本 2 到版本 3 的迁移时,我们检查插入到版本 2 的数据的有效性,也是我们新列的默认值。我们的 AndroidJUnitTest 看起来是这样的:
- @Test
- public void migrationFrom2To3_containsCorrectData() throws
- IOException {
- // Create the database in version 2
- SupportSQLiteDatabase db =
- testHelper.createDatabase(TEST_DB_NAME, 2);
- // Insert some data
- insertUser(USER.getId(), USER.getUserName(), db);
- //Prepare for the next version
- db.close();
- // Re-open the database with version 3 and provide MIGRATION_1_2
- // and MIGRATION_2_3 as the migration process.
- testHelper.runMigrationsAndValidate(TEST_DB_NAME, 3,
- validateDroppedTables, MIGRATION_1_2, MIGRATION_2_3);
- // MigrationTestHelper automatically verifies the schema
- //changes, but not the data validity
- // Validate that the data was migrated properly.
- User dbUser = getMigratedRoomDatabase().userDao().getUser();
- assertEquals(dbUser.getId(), USER.getId());
- assertEquals(dbUser.getUserName(), USER.getUserName());
- // The date was missing in version 2, so it should be null in
- //version 3
- assertEquals(dbUser.getDate(), null);
- }
测试从 SQLiteDatabase 到 Room 的迁移
从标准 SQLiteDatabase 到 Room 的步骤虽然乍一看很直观,但我们觉得有必要详细介绍如何测试迁移实现。
因为原本的数据库没有使用 Room 实现,自然我们就没有相应的 JSON 文件,因此我们无法使用 MigrationTestHelper 创建数据库。
我们需要这么做:
- 扩展 SQLiteOpenHelper 类,并在 onCreate 执行创建数据库表的 SQL 查询操作;
- 在 @Before 测试方法中,创建数据库;
- 在 @After 测试方法中,清除数据库;
- 使用 SQLiteOpenHelper ,来插入测试所需的数据,检查从SQLiteDatabase 版本迁移到使用 Room 的版本;
- 使用 MigrationTestHelper 运行迁移和验证 schema;
- 检查数据库数据的有效性。
数据库版本 1 使用 SQLiteDatabase 实现,然后在版本 2 中,我们迁移到了 Room,而在版本 3 中,我们添加了一个新的列。检查从版本 1 到 3 的迁移测试如下所示:
- @Test
- public void migrationFrom1To3_containsCorrectData() throws IOException {
- // Create the database with the initial version 1 schema and
- //insert a user
- SqliteDatabaseTestHelper.insertUser(1, USER.getUserName(), sqliteTestDbHelper);
- // Re-open the database with version 3 and provide MIGRATION_1_2
- // and MIGRATION_2_3 as the migration process.
- testHelper.runMigrationsAndValidate(TEST_DB_NAME, 3, true,
- MIGRATION_1_2, MIGRATION_2_3);
- // Get the latest, migrated, version of the database
- // Check that the correct data is in the database
- User dbUser = getMigratedRoomDatabase().userDao().getUser();
- assertEquals(dbUser.getId(), 1);
- assertEquals(dbUser.getUserName(), USER.getUserName());
- // The date was missing in version 2, so it should be null in
- //version 3
- assertEquals(dbUser.getDate(), null);
- }
挽起袖子试试吧!
这里有一个示例 App:
https://github.com/googlesamples/android-architecture-components/tree/master/PersistenceMigrationsSample
您可以在此示例应用中查看实现。为了简化比较,每个数据库版本都是以自己的风格实现的,相信看完之后您已经能玩转典型的迁移路径了:
- sqlite:使用 SQLiteOpenHelper 和传统的 SQLite 界面;
- room :使用 Room 替换实现,并提供到版本 2 的迁移;
- room2:将数据库更新为新 schema,版本 3;
- room3: 将数据库更新为新的版本 4。提供从版本 2 到 3,版本 3 到 4,以及版本 1 到 4 的迁移路径。
使用 Room,您可以很容易地实施和测试迁移。MigrationTestHelper 允许您在任何版本打开数据库、运行迁移,并且只需几行代码就可以验证 schema。
【本文是51CTO专栏机构“谷歌开发者”的原创稿件,转载请联系原作者(微信公众号:Google_Developers)】