Android O 遷移測(cè)試:Room
在之前的一篇文章中,我們介紹了一下構(gòu)件組件,其中就提到了使用 Room 來進(jìn)行數(shù)據(jù)庫(kù)操作。但我們也有注意到,如果您錯(cuò)誤地實(shí)施遷移,最嚴(yán)重的結(jié)果可能會(huì)導(dǎo)致您的應(yīng)用崩潰或丟失用戶數(shù)據(jù)。
除此之外,Migration.migrate 在編譯時(shí)不會(huì)檢查在方法中執(zhí)行的 SQL 語句,這導(dǎo)致了更多的問題。了解到這個(gè)情況之后,對(duì)遷移進(jìn)行測(cè)試就成了一項(xiàng)必做任務(wù)。 Room 提供了 MigrationTestHelper 測(cè)試工具,這一工具允許您:
- 在給定的版本中創(chuàng)建一個(gè)數(shù)據(jù)庫(kù)
- 在數(shù)據(jù)庫(kù)上運(yùn)行一組給定的遷移
- 驗(yàn)證數(shù)據(jù)庫(kù) schema
當(dāng)然,Room 不會(huì)驗(yàn)證數(shù)據(jù)庫(kù)中的數(shù)據(jù)本身。這是您需要親自去實(shí)現(xiàn)的東西。
以下是您需要了解的有關(guān) Room 遷移測(cè)試的內(nèi)容。
工作原理
為了進(jìn)行遷移測(cè)試,Room 需要知道您當(dāng)前數(shù)據(jù)庫(kù)版本的幾個(gè)要素:版本號(hào)、Entity、Identity Hash,以及為創(chuàng)建和更新 room_master_table 而做出的查詢請(qǐng)求。所有這些都由 Room 在編譯時(shí)自動(dòng)生成,并存儲(chǔ)在schema JSON 文件中。
在 build.gradle 文件中指定一個(gè)文件夾,來放置這些生成的 schema JSON 文件。在更新 schema 時(shí),最終會(huì)出現(xiàn)一些 JSON 文件,每個(gè)版本都將有一個(gè)對(duì)應(yīng)的文件。確保將每個(gè)生成的文件提交給源代碼管理工具。下次再次增加版本號(hào)碼時(shí),Room 可以使用 JSON 文件進(jìn)行測(cè)試。
先決條件
要生成 JSON 文件,請(qǐng)使用以下內(nèi)容更新 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 測(cè)試庫(kù)添加到依賴列表中
- dependencies {
- androidTestImplementation
- “android.arch.persistence.room:testing:1.0.0-alpha5”
- }
遷移測(cè)試規(guī)則
創(chuàng)建數(shù)據(jù)庫(kù)和 schema,打開和關(guān)閉數(shù)據(jù)庫(kù),運(yùn)行遷移 —— 您幾乎需要為每個(gè)測(cè)試編寫大量這樣的樣板代碼。為了避免過度重復(fù)勞動(dòng),請(qǐng)?jiān)谶w移測(cè)試類中使用 MigrationTestHelper 測(cè)試工具。
為了創(chuàng)建數(shù)據(jù)庫(kù)以及驗(yàn)證遷移,MigrationTestHelper 很大程度上依賴于生成的 JSON 文件。
- @Rule
- public MigrationTestHelper testHelper =
- new MigrationTestHelper(
- InstrumentationRegistry.getInstrumentation(),
- <your_database_class>.class.getCanonicalName(),
- new FrameworkSQLiteOpenHelperFactory()
您可以在特定版本中創(chuàng)建數(shù)據(jù)庫(kù):
- // Create the database with version 2
- SupportSQLiteDatabase db =
- testHelper.createDatabase(TEST
您可以運(yùn)行一組遷移,并自動(dòng)驗(yàn)證 schema 是否更新無誤:
- db = testHelper.runMigrationsAndValidate(TEST_DB_NAME, 4, validateDroppe
實(shí)施測(cè)試
測(cè)試策略很簡(jiǎn)單:
- 在特定版本中打開數(shù)據(jù)庫(kù);
- 插入一些數(shù)據(jù);
- 運(yùn)行遷移并驗(yàn)證 schema;
- 檢查數(shù)據(jù)庫(kù)中是否有正確的數(shù)據(jù)。
例如,數(shù)據(jù)庫(kù)的版本 3 添加了一個(gè)新列:date 。因此,當(dāng)測(cè)試從版本 2 到版本 3 的遷移時(shí),我們檢查插入到版本 2 的數(shù)據(jù)的有效性,也是我們新列的默認(rèn)值。我們的 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);
- }
測(cè)試從 SQLiteDatabase 到 Room 的遷移
從標(biāo)準(zhǔn) SQLiteDatabase 到 Room 的步驟雖然乍一看很直觀,但我們覺得有必要詳細(xì)介紹如何測(cè)試遷移實(shí)現(xiàn)。
因?yàn)樵镜臄?shù)據(jù)庫(kù)沒有使用 Room 實(shí)現(xiàn),自然我們就沒有相應(yīng)的 JSON 文件,因此我們無法使用 MigrationTestHelper 創(chuàng)建數(shù)據(jù)庫(kù)。
我們需要這么做:
- 擴(kuò)展 SQLiteOpenHelper 類,并在 onCreate 執(zhí)行創(chuàng)建數(shù)據(jù)庫(kù)表的 SQL 查詢操作;
- 在 @Before 測(cè)試方法中,創(chuàng)建數(shù)據(jù)庫(kù);
- 在 @After 測(cè)試方法中,清除數(shù)據(jù)庫(kù);
- 使用 SQLiteOpenHelper ,來插入測(cè)試所需的數(shù)據(jù),檢查從SQLiteDatabase 版本遷移到使用 Room 的版本;
- 使用 MigrationTestHelper 運(yùn)行遷移和驗(yàn)證 schema;
- 檢查數(shù)據(jù)庫(kù)數(shù)據(jù)的有效性。
數(shù)據(jù)庫(kù)版本 1 使用 SQLiteDatabase 實(shí)現(xiàn),然后在版本 2 中,我們遷移到了 Room,而在版本 3 中,我們添加了一個(gè)新的列。檢查從版本 1 到 3 的遷移測(cè)試如下所示:
- @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);
- }
挽起袖子試試吧!
這里有一個(gè)示例 App:
https://github.com/googlesamples/android-architecture-components/tree/master/PersistenceMigrationsSample
您可以在此示例應(yīng)用中查看實(shí)現(xiàn)。為了簡(jiǎn)化比較,每個(gè)數(shù)據(jù)庫(kù)版本都是以自己的風(fēng)格實(shí)現(xiàn)的,相信看完之后您已經(jīng)能玩轉(zhuǎn)典型的遷移路徑了:
- sqlite:使用 SQLiteOpenHelper 和傳統(tǒng)的 SQLite 界面;
- room :使用 Room 替換實(shí)現(xiàn),并提供到版本 2 的遷移;
- room2:將數(shù)據(jù)庫(kù)更新為新 schema,版本 3;
- room3: 將數(shù)據(jù)庫(kù)更新為新的版本 4。提供從版本 2 到 3,版本 3 到 4,以及版本 1 到 4 的遷移路徑。
使用 Room,您可以很容易地實(shí)施和測(cè)試遷移。MigrationTestHelper 允許您在任何版本打開數(shù)據(jù)庫(kù)、運(yùn)行遷移,并且只需幾行代碼就可以驗(yàn)證 schema。
【本文是51CTO專欄機(jī)構(gòu)“谷歌開發(fā)者”的原創(chuàng)稿件,轉(zhuǎn)載請(qǐng)聯(lián)系原作者(微信公眾號(hào):Google_Developers)】