自動備份與還原測試

使用者設定新裝置或重新安裝應用程式時,Android 可以從雲端備份或裝置間傳輸還原應用程式資料。如果備份規則遺漏重要資料,或應用程式無法使用還原的資料,使用者就會遺失設定和進度,且不會看到錯誤。

自動備份和還原測試可協助您在發布應用程式前找出這些問題。這些測試是 JUnit 5 測試,會在開發機器上執行,並透過 Android Debug Bridge (adb) 控制連結裝置。每項測試都會在裝置上植入應用程式資料、透過 Android 備份架構備份及還原應用程式,然後檢查資料是否完整。您可以在模組的建構檔案中,將測試設為 Android Gradle 外掛程式 (AGP) 測試套件。

運作方式

備份與還原測試套件包含兩個部分,兩者會搭配運作:

  • 主機測試是在開發機器上執行的 JUnit 5 測試。他們會為每部裝置使用 BackupRestoreController 來植入資料、備份及還原應用程式,並查看結果。
  • 裝置動作是實作 BackupDeviceAction 的類別。這些測試會在裝置上應用程式的程序中執行,因此可以透過應用程式本身的程式碼讀取及寫入資料。主機測試會依類別名稱呼叫裝置動作。

執行套件時,AGP 會建構應用程式和包含裝置動作的測試 APK。測試架構會在測試使用的每部裝置上安裝這兩個 APK,並在每次測試前清除應用程式資料。

內建儲存空間類型

下表列出 StorageDomain 子類別,可讓您植入及驗證資料,而不必編寫裝置動作:

儲存空間網域 資料
StorageDomain.Preference SharedPreferences 檔案中的鍵。
StorageDomain.DataStorePreference Preferences DataStore 檔案中的鍵。
StorageDomain.Database SQLite 資料庫資料表中的資料列。
StorageDomain.TextFile 應用程式內部儲存空間中的文字檔案。
StorageDomain.BinaryFile 應用程式內部儲存空間中的二進位檔案。
StorageDomain.ExcludedData 另一個內建儲存空間網域,其資料不應還原,例如備份規則排除的資料。

如要測試其他資料 (例如 Proto DataStore 檔案或 Room 資料庫),請編寫裝置動作。

交通方式

每個備份都會使用 BackupTransportMode 值。下表列出支援的交通方式:

模式 情境
LOCAL 備份至裝置的本機備份傳輸。備份資料會保留在裝置上。
CLOUD_ENCRYPTED 雲端備份,例如從啟用用戶端加密功能的裝置,儲存到使用者 Google 帳戶的備份。
CLOUD_UNENCRYPTED 未啟用用戶端加密功能的裝置雲端備份。
DEVICE_TO_DEVICE 裝置對裝置轉移,例如透過傳輸線或 Wi-Fi。

LOCAL 適用於任何支援的裝置。雲端和裝置對裝置模式建立的備份,與 Android Studio 中的「Backup App Data」動作相同:備份會儲存至開發機器,而不是上傳或傳送至其他裝置。雲端和裝置對裝置模式需要裝置上的 Google Play 服務,以及應用程式的可偵錯版本。如果備份規則取決於用戶端加密,請測試兩種雲端模式。

需求條件

如要使用自動備份和還原測試,請確認環境符合下列需求條件:

  • Gradle 9.6.0 以上版本。
  • JDK 17 以上版本。
  • 搭載 Android 12 (API 級別 31) 以上版本的裝置或模擬器,並透過 ADB 連線:
    • 搭載 API 級別 36.1 以上版本的裝置或模擬器,必須使用 Google Play 服務 26.33.30 以上版本。
    • 如要測試兩部裝置之間的轉移作業,請連接兩部裝置。

相容性

下表列出自動備份和還原測試的最低版本和建議版本:

元件 最低版本 建議版本
Android Studio Rabbit 2 Canary 1 Rabbit 2 Canary 4 以上版本
Android Gradle 外掛程式 (AGP) 9.5.0-alpha06 9.5.0-alpha09 以上版本
androidx.test.backup 個程式庫 1.0.0-alpha01 1.0.0-alpha04 以上版本

每個 AGP 預先發布版都需要搭配相應的 Android Studio 預先發布版。詳情請參閱 AGP 預先發布版。

設定專案

如要在應用程式模組中新增備份和還原測試套件,請完成下列步驟:

啟用實驗性旗標

在專案的 gradle.properties 檔案中,啟用測試套件和備份測試:

android.experimental.testSuiteSupport=true
android.experimental.enableBackupTest=true

宣告測試套件

在應用程式模組的 build.gradle.kts 檔案中,於 testOptions 區塊宣告備份測試套件:

android {
    testOptions {
        backupTests {
            create("backupTest") {
                backupTestLibraryVersion = "1.0.0-alpha04"
            }
        }
    }
}

套件名稱 (在本例中為 backupTest) 會決定來源集目錄和 Gradle 工作名稱。您可以使用下列選用屬性自訂套件:

  • 使用 backupTestLibraryVersion 設定 AGP 新增至套件的 androidx.test.backup 程式庫版本,例如 "1.0.0-alpha04"。這是選用屬性。如果省略,AGP 會根據 AGP 版本,改用預設程式庫版本。本頁範例需要 1.0.0-alpha04 以上版本。
  • 使用 targetVariants 指定套件測試的建構變化版本,例如 targetVariants.add("demoDebug")。這是選用屬性。如果省略,套件會測試模組 testBuildType 的變體,預設為 debug。建議您測試可偵錯的變體,因為雲端和裝置對裝置傳輸模式需要可偵錯的應用程式。

AGP 會將主機程式庫 androidx.test.backup:backup-host 和 JUnit 5 新增至主機測試。這會將裝置程式庫 androidx.test.backup:backup 新增至測試 APK,並設定測試 APK 的檢測設備執行器。

如要新增其他依附元件,請使用套件的 hostJar 和 testApk 區塊。舉例來說,如要在主機測試中使用 Truth,請將其新增至 hostJar 區塊:

create("backupTest") {
    hostJar {
        dependencies {
            implementation("com.google.truth:truth:1.4.2")
        }
    }
}

建立來源集

建立與套件名稱相符的來源集目錄:

  • 將主機測試放入 src/{suiteName}/test/kotlin/。
  • 將裝置動作放入 src/{suiteName}/androidTest/kotlin/。

舉例來說,名為 backupTest 的套件會使用下列目錄結構:

app/src/backupTest/
├── androidTest/
│   └── kotlin/com/example/myapp/ThemeActions.kt
└── test/
    └── kotlin/com/example/myapp/SettingsBackupTest.kt

檢查備份設定

測試會備份及還原應用程式備份設定包含的資料。請確認應用程式已啟用備份功能,且備份規則包含您測試的資料。

編寫備份與還原測試

在套件的 test 來源集中編寫主機測試。使用 @ExtendWith(BackupRestoreExtension::class) 為每個測試類別加上註解,為測試提供連結裝置。測試會針對套件指定的應用程式變體執行,因此您不需要設定其應用程式 ID。

每個測試方法都會為其使用的每部裝置宣告 BackupRestoreController 參數。

測試內建儲存空間類型

如要測試內建儲存空間類型,請呼叫 runBackupRestoreFlow,並提供要植入的資料、備份檔案的目錄和傳輸模式。方法會在裝置上執行下列步驟:

  1. 為每個儲存空間網域提供種子。
  2. 停止應用程式。
  3. 備份應用程式。
  4. 清除應用程式的資料。
  5. 還原備份。
  6. 確認每個儲存空間網域都有種子值,或在包裝於 StorageDomain.ExcludedData 時不存在。

如果步驟失敗,方法會擲回例外狀況,導致測試失敗。以下範例使用本機備份傳輸測試 SharedPreferences、Preferences DataStore、SQLite、內部檔案和排除檔案:

package com.example.myapp

import androidx.test.backup.host.BackupRestoreController
import androidx.test.backup.host.BackupRestoreExtension
import androidx.test.backup.host.BackupTransportMode
import androidx.test.backup.host.StorageDomain
import java.nio.file.Path
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.junit.jupiter.api.io.TempDir

@ExtendWith(BackupRestoreExtension::class)
class SettingsBackupTest {

    @Test
    fun settingsSurviveBackupAndRestore(
        device: BackupRestoreController,
        @TempDir outputDir: Path,
    ): Unit = runBlocking {
        device.runBackupRestoreFlow(
            storages = listOf(
                StorageDomain.Preference(
                    prefName = "settings",
                    key = "channels",
                    value = setOf("news", "alerts"),
                ),
                StorageDomain.DataStorePreference(
                    dataStoreName = "user_prefs",
                    key = "font_scale",
                    value = 1.25f,
                ),
                StorageDomain.Database(
                    dbName = "notes.db",
                    table = "notes",
                    primaryKeyCol = "id",
                    primaryKeyVal = 1,
                    columnValues = mapOf("title" to "Groceries"),
                ),
                StorageDomain.TextFile(
                    path = "drafts/note.txt",
                    content = "Buy milk",
                ),
                // Check that a file excluded by backup rules isn't restored.
                StorageDomain.ExcludedData(
                    StorageDomain.TextFile(
                        path = "auth/session.txt",
                        content = "temp-token",
                    )
                ),
            ),
            outputDir = outputDir,
            mode = BackupTransportMode.LOCAL,
        )
    }
}

定義儲存空間網域時,請留意下列事項:

  • Preference 會採用 SharedPreferences 檔案的名稱,但不含 .xml 副檔名。
  • DataStorePreference 會採用傳遞至 preferencesDataStore 的名稱。請為 value 使用與應用程式偏好設定鍵相同的 Kotlin 類型,例如 floatPreferencesKey 的 1.25f,以便應用程式讀取還原的項目。
  • TextFile 和 BinaryFile 會根據應用程式的 filesDir 解析相對路徑。
  • Database 會使用 SQL 寫入資料列,如果資料表不存在,則會建立含有 TEXT 欄的資料表。如果應用程式使用 Room,請透過使用 DAO 的裝置動作測試資料庫內容,確保資料庫符合 Room 的結構定義。
  • ExcludedData 至少需要一個未排除的儲存空間網域,且該網域必須位於同一個 runBackupRestoreFlow 呼叫中。
  • 設定 isDeviceProtectedStorage = true 時,Preference、DataStorePreference、Database、TextFile 和 BinaryFile 會指定裝置保護的儲存空間,而非應用程式的預設儲存空間。

寫入裝置動作

如要植入或驗證內建儲存空間類型未涵蓋的資料,請在套件的 androidTest 來源集中寫入裝置動作。實作 BackupDeviceAction 並覆寫 execute,使用應用程式的 Context 和主機測試的引數執行動作。傳回使用 BackupDeviceActionResult.success 或 BackupDeviceActionResult.failure 建立的 BackupDeviceActionResult,並將錯誤訊息傳遞至 failure。

execute 方法會在應用程式程序的背景執行緒中執行,因此可以封鎖,直到讀取和寫入作業完成。如要存取測試執行器的檢測,例如 UiAutomation,請呼叫 InstrumentationRegistry.getInstrumentation。

以下範例會在 Proto DataStore 中植入並驗證主題設定,其中 settingsDataStore 是應用程式中定義的 DataStore<Settings> 屬性:

// In app/src/backupTest/androidTest/kotlin/com/example/myapp/ThemeActions.kt
package com.example.myapp

import android.content.Context
import androidx.test.backup.BackupDeviceAction
import androidx.test.backup.BackupDeviceActionArgs
import androidx.test.backup.BackupDeviceActionResult
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking

class SeedThemeAction : BackupDeviceAction {
    override fun execute(
        context: Context,
        args: BackupDeviceActionArgs,
    ): BackupDeviceActionResult {
        val theme = args["theme"]
            ?: return BackupDeviceActionResult.failure("Missing theme argument")
        runBlocking {
            context.settingsDataStore.updateData {
                it.toBuilder().setTheme(theme).build()
            }
        }
        return BackupDeviceActionResult.success()
    }
}

class VerifyThemeAction : BackupDeviceAction {
    override fun execute(
        context: Context,
        args: BackupDeviceActionArgs,
    ): BackupDeviceActionResult {
        val expected = args["theme"]
        val actual = runBlocking {
            context.settingsDataStore.data.first().theme
        }
        return if (actual == expected) {
            BackupDeviceActionResult.success()
        } else {
            BackupDeviceActionResult.failure("Expected $expected, found $actual")
        }
    }
}

如要在 runBackupRestoreFlow 中執行填入和驗證動作,請將其類別名稱傳遞至 StorageDomain.CustomData:

package com.example.myapp

import androidx.test.backup.host.BackupRestoreController
import androidx.test.backup.host.BackupRestoreExtension
import androidx.test.backup.host.BackupTransportMode
import androidx.test.backup.host.StorageDomain
import java.nio.file.Path
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.junit.jupiter.api.io.TempDir

const val SEED_THEME = "com.example.myapp.SeedThemeAction"
const val VERIFY_THEME = "com.example.myapp.VerifyThemeAction"

@ExtendWith(BackupRestoreExtension::class)
class ThemeBackupTest {

    @Test
    fun themeSurvivesCloudBackup(
        device: BackupRestoreController,
        @TempDir outputDir: Path,
    ): Unit = runBlocking {
        device.runBackupRestoreFlow(
            storage = StorageDomain.CustomData(
                populateAction = SEED_THEME,
                verifyAction = VERIFY_THEME,
                args = mapOf("theme" to "dark"),
            ),
            outputDir = outputDir,
            mode = BackupTransportMode.CLOUD_ENCRYPTED,
        )
    }
}

如要將播種期間產生的值傳遞至驗證動作,請將對應傳遞至 BackupDeviceActionResult.success。CustomData 會將這些項目合併至驗證動作的引數。

執行流程的每個步驟

如要自訂流程 (例如呼叫 uninstallApp 和 installApk 測試升級,或呼叫 launchApp),請自行執行每個步驟:

package com.example.myapp

import androidx.test.backup.host.BackupRestoreController
import androidx.test.backup.host.BackupRestoreExtension
import androidx.test.backup.host.BackupTransportMode
import java.nio.file.Path
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.junit.jupiter.api.io.TempDir

@ExtendWith(BackupRestoreExtension::class)
class CustomFlowBackupTest {

    @Test
    fun themeSurvivesLaunchAfterRestore(
        device: BackupRestoreController,
        @TempDir outputDir: Path,
    ): Unit = runBlocking {
        val args = mapOf("theme" to "dark")
        device.runOnDevice(SEED_THEME, args).getOrThrow()

        device.stopApp()
        val backupFile = device.performBackup(
            BackupTransportMode.CLOUD_ENCRYPTED,
            outputDir,
        )
        device.clearAppData()
        device.performRestore(backupFile)
        device.launchApp()

        device.runOnDevice(VERIFY_THEME, args).getOrThrow()
    }
}

當裝置動作失敗時,runOnDevice 會傳回 BackupActionResult.Failure,而不是擲回例外狀況。呼叫 getOrThrow,在成功時取得傳回的 data 對應,或在失敗時擲回 IOException。

測試裝置間的轉移作業

如要測試兩部裝置之間的轉移作業,請為每部裝置宣告 BackupRestoreController 參數,並將目標裝置傳遞至 runBackupRestoreFlow 中的 restoreOn:

package com.example.myapp

import androidx.test.backup.host.BackupRestoreController
import androidx.test.backup.host.BackupRestoreExtension
import androidx.test.backup.host.BackupTransportMode
import androidx.test.backup.host.Device
import androidx.test.backup.host.StorageDomain
import java.nio.file.Path
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.junit.jupiter.api.io.TempDir

@ExtendWith(BackupRestoreExtension::class)
class ThemeTransferTest {

    @Test
    fun themeTransfersToNewDevice(
        @Device(role = "source") source: BackupRestoreController,
        @Device(role = "target") target: BackupRestoreController,
        @TempDir outputDir: Path,
    ): Unit = runBlocking {
        source.runBackupRestoreFlow(
            storage = StorageDomain.DataStorePreference(
                dataStoreName = "settings",
                key = "theme",
                value = "dark",
            ),
            outputDir = outputDir,
            mode = BackupTransportMode.DEVICE_TO_DEVICE,
            restoreOn = target,
        )
    }
}

在 source 上,流程會植入並備份應用程式。在 target 上,流程會清除應用程式資料、還原備份,並驗證資料。如要在其他裝置上還原,則需要 DEVICE_TO_DEVICE 或雲端傳輸模式,因為 LOCAL 備份資料會保留在來源裝置上。

@Device 的 role 值可讓您在執行測試時為每個參數選擇裝置。如果只有一部相符的裝置連線,這兩個參數會共用該裝置,並在單一裝置上模擬傳輸。

選擇裝置

根據預設,測試架構會依序號排序線上裝置,並在連接足夠裝置時,為每個 BackupRestoreController 參數提供各自的相符裝置。無法取得個別裝置的參數會共用第一個相符的裝置。ANDROID_SERIAL 環境變數不會影響裝置指派。

如要選擇裝置,但不在測試中加入序號,請使用 @Device(role = "...") 為每個裝置參數指派角色。接著,在執行測試時,使用 androidx.test.backup.device.serial.{role} 系統屬性將每個角色繫結至裝置。舉例來說,下列指令會執行套件,並將 emulator-5554 設為來源裝置,emulator-5556 設為目標裝置:

./gradlew :app:testDebugBackupTestDefaultTestSuite -Dandroidx.test.backup.device.serial.source=emulator-5554 -Dandroidx.test.backup.device.serial.target=emulator-5556

繫結只會套用至具有該角色的參數。其他參數則會取得其餘相符的裝置。

您也可以在 @Device 上使用 serial、api、minApi 和 maxApi 篩選裝置,例如 @Device(role = "target", minApi = 35)。如要透過指令列設定序號或確切 API 級別,請使用 -D 傳遞下列系統屬性:

系統屬性 說明
androidx.test.backup.device.serial.{role} 裝置序號,用於加上 @Device(role = "{role}") 註解的參數。優先順序高於 serials、serial 和 @Device 的 serial 值。
androidx.test.backup.device.serials 以半形逗號分隔的序號清單,依序指派給裝置參數。優先順序高於 serial 和 @Device 的 serial 值。
androidx.test.backup.device.serial serials 未涵蓋的裝置參數序號。優先順序高於 @Device 的 serial 值。
androidx.test.backup.device.apis 以半形逗號分隔的 API 級別清單,會依序指派給裝置參數。使用 0 可比對任何 API 級別。在 minApi 和 maxApi 內,取代 @Device 上的 api。
androidx.test.backup.device.api apis 未涵蓋的裝置參數 API 級別。使用 0 可比對任何 API 級別。在 @Device、minApi 和 maxApi 中取代 api。

serial、serials、api 和 apis 屬性適用於執行的每項測試,因此請使用 --tests 選項,只執行需要這些屬性的測試。

測試還原憑證

如果應用程式使用還原憑證,請測試還原金鑰是否能在備份和還原後保留。雲端和裝置對裝置模式會備份金鑰和應用程式資料,但 LOCAL 不會。

編寫裝置動作,建立金鑰並透過 Credential Manager 取得。建立動作會傳回金鑰的憑證 ID,因此驗證動作可以檢查還原作業是否帶回相同的金鑰。在這個範例中,createRequestJson 和 getRequestJson 是應用程式傳遞至 Credential Manager 的要求 JSON 字串:

// In app/src/backupTest/androidTest/kotlin/com/example/myapp/RestoreKeyActions.kt
package com.example.myapp

import android.content.Context
import androidx.credentials.CreateRestoreCredentialRequest
import androidx.credentials.CreateRestoreCredentialResponse
import androidx.credentials.CredentialManager
import androidx.credentials.GetCredentialRequest
import androidx.credentials.GetRestoreCredentialOption
import androidx.credentials.RestoreCredential
import androidx.test.backup.BackupDeviceAction
import androidx.test.backup.BackupDeviceActionArgs
import androidx.test.backup.BackupDeviceActionResult
import kotlinx.coroutines.runBlocking
import org.json.JSONObject

class CreateRestoreKeyAction : BackupDeviceAction {
    override fun execute(
        context: Context,
        args: BackupDeviceActionArgs,
    ): BackupDeviceActionResult {
        val request = CreateRestoreCredentialRequest(createRequestJson)
        val response = runBlocking {
            CredentialManager.create(context).createCredential(context, request)
        } as CreateRestoreCredentialResponse
        val credentialId = JSONObject(response.responseJson).getString("id")
        return BackupDeviceActionResult.success(mapOf("credential_id" to credentialId))
    }
}

class VerifyRestoreKeyAction : BackupDeviceAction {
    override fun execute(
        context: Context,
        args: BackupDeviceActionArgs,
    ): BackupDeviceActionResult {
        val option = GetRestoreCredentialOption(getRequestJson)
        val response = runBlocking {
            CredentialManager.create(context)
                .getCredential(context, GetCredentialRequest(listOf(option)))
        }
        val credential = response.credential as RestoreCredential
        val credentialId = JSONObject(credential.authenticationResponseJson).getString("id")
        val expected = args["credential_id"]
        return if (credentialId == expected) {
            BackupDeviceActionResult.success()
        } else {
            BackupDeviceActionResult.failure("Expected key $expected, found $credentialId")
        }
    }
}

下列主機測試會在來源裝置上建立金鑰、在目標裝置上還原金鑰,並驗證憑證 ID 是否相符:

package com.example.myapp

import androidx.test.backup.host.BackupRestoreController
import androidx.test.backup.host.BackupRestoreExtension
import androidx.test.backup.host.BackupTransportMode
import androidx.test.backup.host.Device
import androidx.test.backup.host.StorageDomain
import java.nio.file.Path
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.junit.jupiter.api.io.TempDir

const val CREATE_RESTORE_KEY = "com.example.myapp.CreateRestoreKeyAction"
const val VERIFY_RESTORE_KEY = "com.example.myapp.VerifyRestoreKeyAction"

@ExtendWith(BackupRestoreExtension::class)
class RestoreKeyTest {

    @Test
    fun restoreKeyTransfersToNewDevice(
        @Device(role = "source") source: BackupRestoreController,
        @Device(role = "target") target: BackupRestoreController,
        @TempDir outputDir: Path,
    ): Unit = runBlocking {
        // Clears target (including any existing restore key) before restoring.
        source.runBackupRestoreFlow(
            storage = StorageDomain.CustomData(
                populateAction = CREATE_RESTORE_KEY,
                verifyAction = VERIFY_RESTORE_KEY,
            ),
            outputDir = outputDir,
            mode = BackupTransportMode.DEVICE_TO_DEVICE,
            restoreOn = target,
        )
    }
}

如要建立 isCloudBackupEnabled 設為 true (預設) 的金鑰,來源裝置必須具備 Google 帳戶,且已開啟備份功能和螢幕鎖定。否則,Credential Manager 會擲回 E2eeUnavailableException。

與在新裝置上進行雲端還原不同,雲端模式不需要相同的 Google 帳戶或來源裝置的螢幕鎖定,且會還原 isCloudBackupEnabled 設為 false 的金鑰。

在測試之間保留應用程式資料

每次測試前,測試架構都會清除測試所用裝置上的應用程式資料。如要為測試類別或方法關閉這項功能,請使用 @Isolation(IsolationPolicy.MANUAL) 加上註解。

以 Java 撰寫測試

BackupRestoreController 方法是 Kotlin 暫停函式。如果您使用 Java 編寫主機測試,請使用每個方法的 Async 變體,例如 runBackupRestoreFlowAsync,這會傳回 ListenableFuture。

執行備份與還原測試

AGP 會為套件測試的每個變體建立 Gradle 工作,名為 test{Variant}{SuiteName}DefaultTestSuite。如要針對 debug 變體執行 backupTest 套件,請連結裝置並執行以下工作:

./gradlew :app:testDebugBackupTestDefaultTestSuite

check、test 和 connectedCheck 工作不會執行備份和還原測試套件,因此請明確執行套件的工作。

如要執行單一測試類別或方法,請使用 --tests 選項:

./gradlew :app:testDebugBackupTestDefaultTestSuite --tests "com.example.myapp.ThemeBackupTest"

檢查測試報告

測試執行完畢後,AGP 會在 {module}/build/reports/tests/{taskName}/index.html 產生 HTML 測試報告,例如 app/build/reports/tests/testDebugBackupTestDefaultTestSuite/index.html。

測試失敗時,測試架構會儲存測試所用各裝置的 logcat,並在失敗輸出內容中列出儲存的檔案路徑。如要關閉記錄儲存功能,請傳遞 -Dandroidx.test.backup.device.logs.enabled=false。

排解還原失敗問題

在下列情況下,雲端或裝置對裝置還原作業會失敗,並顯示錯誤訊息:

  • Google Play 服務略過您的應用程式,例如應用程式 ID 開頭為 com.google. 或 com.android.、備份內容空白,或設定 android:allowBackup="false"。如要測試這類應用程式 ID 的應用程式,請使用 LOCAL 模式。
  • 在搭載 API 級別 36.1 以上版本的裝置或模擬器上,Google Play 服務版本低於 26.33.30。請在裝置上透過 Play 商店更新 Google Play 服務,或使用 26.33.30 以上版本的系統映像檔。

已知問題和限制

自動備份與還原測試有下列已知問題和限制:

  • Android Studio 尚未支援測試套件。即使專案設定正確,程式碼編輯器仍可能在測試套件來源檔案中顯示無法解析的參照錯誤。請改用 Gradle,透過指令列建構及執行備份與還原測試。
  • 測試只會在透過 ADB 連線至開發機器的裝置和模擬器上執行。不支援 Firebase Test Lab。
  • 測試會清除應用程式資料,並變更所用裝置的備份設定。建議使用專用測試裝置。

如要瞭解各版本的異動,請參閱備份與還原測試版本資訊。