Автоматическое тестирование резервного копирования и восстановления

Когда пользователь настраивает новое устройство или переустанавливает приложение, Android может восстановить данные приложения из облачной резервной копии или путем переноса с другого устройства. Если правила резервного копирования не учитывают важные данные или приложение не может использовать восстановленные данные, пользователи потеряют свои настройки и прогресс, не увидев ошибки.

Автоматизированные тесты резервного копирования и восстановления помогают выявить эти проблемы до выпуска приложения. Это тесты JUnit 5, которые выполняются на компьютере для разработки и управляют подключенными устройствами через Android Debug Bridge (adb). Каждый тест заполняет приложение данными на устройстве, создает резервную копию и восстанавливает приложение с помощью фреймворка резервного копирования Android, а затем проверяет, не повреждены ли данные. Тесты настраиваются как набор тестов плагина Android Gradle (AGP) в файле сборки модуля.

Принцип работы

Набор тестов для резервного копирования и восстановления состоит из двух частей, которые работают вместе:

  • Тесты хоста – это тесты JUnit 5, которые выполняются на компьютере для разработки. Для каждого устройства они используют BackupRestoreController, чтобы заполнить данные, создать резервную копию приложения, восстановить его и проверить результаты.
  • Действия на устройствах – это классы, которые реализуют BackupDeviceAction. Они выполняются в процессе приложения на устройстве, поэтому могут читать и записывать данные через код приложения. Тесты Host вызывают действия устройства по имени класса.

Когда вы запускаете набор, AGP создает приложение и тестовый APK-файл, содержащий действия на устройстве. Фреймворк тестирования устанавливает оба APK-файла на каждое устройство, используемое для тестирования, и очищает данные приложения перед каждым тестом.

Типы встроенных хранилищ

В таблице ниже перечислены подклассы StorageDomain, которые позволяют заполнять и проверять данные без написания действий на устройстве.

Домен хранилища Данные
StorageDomain.Preference Ключ в файле SharedPreferences.
StorageDomain.Database Строка в таблице базы данных SQLite.
StorageDomain.TextFile Текстовый файл во внутреннем хранилище приложения.
StorageDomain.BinaryFile Двоичный файл во внутреннем хранилище приложения.

Чтобы протестировать другие данные, например файлы DataStore, напишите действия на устройстве.

Способы передвижения

Для каждой резервной копии используется значение BackupTransportMode. В таблице ниже перечислены поддерживаемые способы передвижения.

Режим Сценарий
LOCAL Резервная копия в локальном хранилище устройства. Резервные копии данных остаются на устройстве.
CLOUD_ENCRYPTED Резервная копия в облаке, например сохраненная в аккаунте Google пользователя с устройства с клиентским шифрованием.
CLOUD_UNENCRYPTED Резервная копия в облаке с устройства, на котором не используется клиентское шифрование.
DEVICE_TO_DEVICE Перенос данных с одного устройства на другое, например по кабелю или Wi-Fi.

LOCAL работает на любом поддерживаемом устройстве. Режимы облака и передачи между устройствами создают такие же резервные копии, как и действие Резервное копирование данных приложения в Android Studio. Резервная копия сохраняется на компьютере для разработки, а не загружается или отправляется на другое устройство. Для режимов облака и переноса с устройства на устройство требуются сервисы Google Play на устройстве и отладочная сборка приложения. Если правила резервного копирования зависят от клиентского шифрования, протестируйте оба режима облака.

Требования

Чтобы использовать автоматическое резервное копирование и тестирование восстановления, убедитесь, что ваша среда соответствует следующим требованиям:

  • Gradle версии 9.6.0 или более поздней.
  • JDK версии 17 или выше.
  • Устройство или эмулятор с Android 12 (уровень API 31) или более поздней версии, подключенные через adb:
    • Для устройств и эмуляторов с уровнем API 36.1 или выше требуются сервисы Google Play версии 26.33.30 или выше.
    • Чтобы протестировать передачу данных между двумя устройствами, подключите их.

Совместимость

В таблице ниже приведены минимальные и рекомендуемые версии для автоматического тестирования резервного копирования и восстановления.

Компонент Минимальная версия Рекомендуемая версия
Android Studio Кролик 2, канарейка 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-alpha03 или более поздней версии

Для каждой предварительной версии 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-alpha03"
            }
        }
    }
}

Название набора, в данном примере backupTest, определяет каталоги наборов исходных файлов и названия задач Gradle. Вы можете настроить набор, используя следующие необязательные свойства:

  • Используйте backupTestLibraryVersion, чтобы задать версию библиотек androidx.test.backup, которые AGP добавляет в набор, например "1.0.0-alpha03". Это необязательное свойство. Если вы не укажете версию, AGP будет использовать версию библиотеки по умолчанию, которая зависит от версии AGP. Для примеров на этой странице требуется версия 1.0.0-alpha03 или выше.
  • Используйте 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), которая предоставляет тестам подключенные устройства. Тесты выполняются для варианта приложения, на который нацелен набор, поэтому вам не нужно задавать идентификатор приложения.

Каждый метод тестирования объявляет параметр BackupRestoreController для каждого используемого устройства.

Тестирование встроенных типов хранилищ

Чтобы протестировать встроенные типы хранилищ, вызовите runBackupRestoreFlow с данными для заполнения, каталогом для файла резервной копии и транспортным режимом. Метод выполняет на устройстве следующие действия:

  1. Заполняет каждый домен хранилища.
  2. Останавливает приложение.
  3. Создает резервную копию приложения.
  4. Удаляет данные приложения.
  5. Восстанавливает резервную копию.
  6. Проверяет, что у каждого домена хранилища есть начальное значение.

Если шаг не выполняется, метод выдает исключение, которое приводит к сбою теста. В примере ниже тестируется ключ SharedPreferences, строка 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 = "theme",
                    value = "dark",
                ),
                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",
                ),
            ),
            outputDir = outputDir,
            mode = BackupTransportMode.LOCAL,
        )
    }
}

При определении доменов хранения учитывайте следующее:

  • Preference принимает имя файла SharedPreferences без расширения .xml.
  • TextFile и BinaryFile разрешают относительные пути в соответствии с filesDir вашего приложения.
  • Database записывает строку с помощью SQL и создает таблицу со столбцами TEXT, если она не существует. Если в вашем приложении используется Room, рекомендуем тестировать содержимое базы данных с помощью действий на устройстве, которые используют ваши объекты доступа к данным, чтобы у базы данных была схема, ожидаемая Room.

Запись действий на устройстве

Чтобы заполнить или проверить данные, которые не охватывают встроенные типы хранилищ, напишите действия устройства в наборе исходных файлов androidTest набора тестов. Действие устройства реализует BackupDeviceAction и переопределяет два элемента:

  • Задайте для phase значение BackupDeviceAction.PHASE_POPULATE для действия, которое заполняет данные, или BackupDeviceAction.PHASE_VERIFY для действия, которое проверяет данные.
  • Реализуйте execute, чтобы запустить действие с Context вашего приложения и аргументами из теста хоста. Возвращает результат, созданный с помощью фабричного метода BackupDeviceActionResult.success или BackupDeviceActionResult.failure, и передает сообщение об ошибке в failure.

Метод execute выполняется в фоновом потоке процесса приложения, поэтому он может блокировать выполнение до завершения операций чтения и записи.

В следующем примере заполняется и проверяется настройка темы, которую приложение сохраняет с помощью Preferences DataStore. Приложение определяет DataStore в основном наборе исходных файлов:

// In app/src/main/kotlin/com/example/myapp/Settings.kt
package com.example.myapp

import android.content.Context
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore

val Context.settingsDataStore by preferencesDataStore(name = "settings")
val THEME_KEY = stringPreferencesKey("theme")

Для действий с устройствами используются те же определения:

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

import android.content.Context
import androidx.datastore.preferences.core.edit
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 val phase = BackupDeviceAction.PHASE_POPULATE

    override fun execute(
        context: Context,
        args: BackupDeviceActionArgs,
    ): BackupDeviceActionResult {
        val theme = args["theme"]
            ?: return BackupDeviceActionResult.failure("Missing theme argument")
        runBlocking {
            context.settingsDataStore.edit { it[THEME_KEY] = theme }
        }
        return BackupDeviceActionResult.success()
    }
}

class VerifyThemeAction : BackupDeviceAction {
    override val phase = BackupDeviceAction.PHASE_VERIFY

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

Чтобы вернуть данные в хост-тест, передайте карту методу success. Тест хоста считывает карту из BackupActionResult.Success.data.

Выполните каждый шаг процесса

Чтобы использовать действия на устройстве или настроить процесс, вызывайте каждый шаг самостоятельно. Следующий тест хоста выполняет действия устройства из предыдущего раздела, связанные с резервным копированием и восстановлением в облаке:

package com.example.myapp

import androidx.test.backup.host.BackupActionResult
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.fail
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 {
        val args = mapOf("theme" to "dark")
        device.runOnDevice(SEED_THEME, args).assertSuccess()

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

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

fun BackupActionResult.assertSuccess() {
    if (this is BackupActionResult.Failure) fail(errorMessage)
}

Если действие с устройством не удалось выполнить, runOnDevice возвращает BackupActionResult.Failure, а не выдает исключение. Проверьте результат. В остальных случаях при сбое выдается исключение.

Тестирование передачи данных между устройствами

Чтобы протестировать передачу между двумя устройствами, объявите параметр BackupRestoreController для каждого из них. Создайте резервную копию приложения на исходном устройстве, а затем восстановите файл резервной копии на целевом устройстве. В следующем примере повторно используются константы и вспомогательный метод assertSuccess из предыдущего примера:

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 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 {
        val args = mapOf("theme" to "dark")
        source.runOnDevice(SEED_THEME, args).assertSuccess()
        source.stopApp()
        val backupFile = source.performBackup(
            BackupTransportMode.DEVICE_TO_DEVICE,
            outputDir,
        )

        target.clearAppData()
        target.performRestore(backupFile)
        target.runOnDevice(VERIFY_THEME, args).assertSuccess()
    }
}

Значения role в @Device позволяют выбрать устройство для каждого параметра при запуске тестов. Если подключено только одно устройство, оба параметра используют его, и тест имитирует передачу на одном устройстве. Это гарантирует, что восстановленные данные будут взяты из резервной копии.

Выберите устройства

По умолчанию тестовый фреймворк сортирует устройства в сети по серийному номеру и по порядку назначает их параметрам 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

Привязка применяется только к параметрам с этой ролью. Другие параметры позволяют получить оставшиеся устройства.

Вы также можете выбрать устройства со значениями serial и api для @Device или со следующими системными свойствами, которые имеют приоритет над значениями @Device:

Свойство системы Описание
androidx.test.backup.device.serial.{role} Серийный номер устройства для параметров, помеченных как @Device(role = "{role}"). Оно имеет приоритет над правилами serials и serial.
androidx.test.backup.device.serials Список серийных номеров, разделенных запятыми, которые присвоены параметрам устройства по порядку.
androidx.test.backup.device.serial Серийный номер устройства для параметров, которые не охватывает serials.
androidx.test.backup.device.apis Список уровней API, разделенный запятыми и назначенный параметрам устройства по порядку.
androidx.test.backup.device.api Уровень API устройства для параметров, которые не охватывает apis.

Передавайте эти свойства с помощью -D таким же образом. Свойства serial, serials, api и apis применяются ко всем выполняемым тестам, поэтому используйте параметр --tests, чтобы запускать только те тесты, которым они нужны.

Тестирование восстановления учетных данных

Если в вашем приложении используется восстановление учетных данных, проверьте, сохраняется ли ключ восстановления после резервного копирования и восстановления. В режимах облачного копирования и переноса с устройства на устройство ключ копируется вместе с данными приложения, а в режиме LOCAL – нет.

Напишите действия устройства, которые создают ключ и получают его с помощью Менеджера учетных данных. Действие create возвращает идентификатор учетных данных ключа, чтобы действие verify могло проверить, что восстановление возвращает тот же ключ. В этом примере createRequestJson и getRequestJson – это строки 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 val phase = BackupDeviceAction.PHASE_POPULATE

    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 val phase = BackupDeviceAction.PHASE_VERIFY

    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")
        }
    }
}

Следующий тест хоста создает ключ на исходном устройстве, восстанавливает его на целевом устройстве и проверяет его там. В нем используется вспомогательная функция assertSuccess из раздела Выполнение каждого шага процесса.

package com.example.myapp

import androidx.test.backup.host.BackupActionResult
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 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 {
        val created = source.runOnDevice(CREATE_RESTORE_KEY)
        created.assertSuccess()
        val credentialId =
            (created as BackupActionResult.Success).data.getValue("credential_id")
        source.stopApp()
        val backupFile = source.performBackup(
            BackupTransportMode.DEVICE_TO_DEVICE,
            outputDir,
        )

        // Also deletes any restore key on the target.
        target.clearAppData()
        target.performRestore(backupFile)
        target.runOnDevice(VERIFY_RESTORE_KEY, mapOf("credential_id" to credentialId))
            .assertSuccess()
    }
}

Чтобы создать ключ, у которого для параметра isCloudBackupEnabled задано значение по умолчанию true, на исходном устройстве должен быть выполнен вход в аккаунт Google с включенным резервным копированием и настроена блокировка экрана. В противном случае Менеджер учетных данных выдаст ошибку E2eeUnavailableException.

В отличие от восстановления из облака на новом устройстве, для облачных режимов не требуется тот же аккаунт Google или способ разблокировки экрана исходного устройства. Они восстанавливают ключи, созданные с помощью isCloudBackupEnabled, для которого задано значение false.

Сохранять данные приложения между тестами

Перед каждым тестом фреймворк удаляет данные приложения на всех устройствах, которые используются для тестирования. Чтобы отключить эту функцию для тестового класса или метода, добавьте к нему аннотацию @Isolation(IsolationPolicy.MANUAL).

Как писать тесты на Java

Методы BackupRestoreController – это функции приостановки Kotlin. Если вы пишете хост-тесты на Java, используйте вариант Async каждого метода, например runBackupRestoreFlowAsync, который возвращает ListenableFuture.

Как запустить тесты резервного копирования и восстановления

AGP создает задачу Gradle для каждого варианта, который тестирует набор, с именем test{Variant}{SuiteName}DefaultTestSuite. Чтобы запустить набор тестов backupTest для варианта debug, подключите устройства и выполните задачу:

./gradlew :app:testDebugBackupTestDefaultTestSuite

Задачи check, test и connectedCheck не запускают наборы тестов для резервного копирования и восстановления, поэтому запустите задачу набора тестов явным образом.

Чтобы запустить один тестовый класс или метод, используйте параметр --tests:

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

Проверка отчетов о тестировании

После завершения тестирования AGP создает отчет в формате HTML по адресу {module}/build/reports/tests/{taskName}/index.html, например app/build/reports/tests/testDebugBackupTestDefaultTestSuite/index.html.

Устранение неполадок при восстановлении

Восстановление из облака или с другого устройства завершается с сообщением об ошибке в следующих случаях:

  • Сервисы Google Play пропускают ваше приложение, например потому что его идентификатор начинается с com.google. или com.android., резервная копия пуста или в нем задан атрибут android:allowBackup="false". Чтобы протестировать приложение с таким идентификатором, используйте режим LOCAL.
  • Версия сервисов Google Play ниже 26.33.30 на устройстве или эмуляторе с уровнем API 36.1 или выше. Обновите сервисы Google Play через Google Play на устройстве или используйте образ системы с версией 26.33.30 или выше.

Известные проблемы и ограничения

При тестировании автоматического резервного копирования и восстановления могут возникать следующие проблемы и ограничения:

  • Android Studio пока не поддерживает наборы тестов. Редактор кода может показывать ошибки неразрешенных ссылок в исходных файлах набора тестов, даже если проект настроен правильно. Вместо этого создавайте и запускайте тесты резервного копирования и восстановления из командной строки с помощью Gradle.
  • Тесты выполняются только на устройствах и эмуляторах, подключенных к компьютеру для разработки через adb. Firebase Test Lab не поддерживается.
  • В ходе тестирования данные приложения удаляются, а настройки резервного копирования на устройствах изменяются. Рекомендуем использовать для тестирования отдельные устройства.

Информацию об изменениях в каждом выпуске можно найти в примечаниях к выпуску тестирования резервного копирования и восстановления.