Saat pengguna menyiapkan perangkat baru atau menginstal ulang aplikasi Anda, Android dapat memulihkan data aplikasi Anda dari pencadangan cloud atau transfer antarperangkat. Jika aturan pencadangan Anda mengabaikan data penting, atau aplikasi Anda tidak dapat menggunakan data yang dipulihkan, pengguna akan kehilangan setelan dan progresnya tanpa melihat error.
Pengujian pencadangan dan pemulihan otomatis membantu Anda mendeteksi masalah ini sebelum Anda merilis aplikasi. Pengujian ini adalah pengujian JUnit 5 yang berjalan di mesin pengembangan dan mengontrol perangkat terhubung melalui Android Debug Bridge (adb). Setiap pengujian mengisi data aplikasi di perangkat, mencadangkan dan memulihkan aplikasi melalui framework pencadangan Android, lalu memeriksa apakah data masih utuh. Anda mengonfigurasi pengujian sebagai suite pengujian plugin Android Gradle (AGP) di file build modul Anda.
Cara kerjanya
Suite pengujian pencadangan dan pemulihan memiliki dua bagian yang bekerja sama:
- Pengujian host adalah pengujian JUnit 5 yang berjalan di mesin pengembangan Anda. Mereka
menggunakan
BackupRestoreControlleruntuk setiap perangkat guna mengisi data, mencadangkan dan memulihkan aplikasi Anda, serta memeriksa hasilnya. - Tindakan perangkat adalah class yang menerapkan
BackupDeviceAction. Pengujian ini berjalan dalam proses aplikasi Anda di perangkat, sehingga dapat membaca dan menulis data melalui kode aplikasi Anda sendiri. Pengujian Host memanggil tindakan perangkat berdasarkan nama class.
Saat Anda menjalankan rangkaian pengujian, AGP akan mem-build aplikasi dan APK pengujian yang berisi tindakan perangkat Anda. Sebelum setiap pengujian, framework pengujian menginstal kedua APK di setiap perangkat yang digunakan pengujian, lalu menghapus data aplikasi Anda.
Jenis penyimpanan bawaan
Tabel berikut mencantumkan subclass StorageDomain yang
memungkinkan Anda menyisipkan dan memverifikasi data tanpa menulis tindakan perangkat:
| Domain penyimpanan | Data |
|---|---|
StorageDomain.Preference |
Kunci dalam file SharedPreferences. |
StorageDomain.Database |
Baris dalam tabel database SQLite. |
StorageDomain.TextFile |
File teks di penyimpanan internal aplikasi Anda. |
StorageDomain.BinaryFile |
File biner di penyimpanan internal aplikasi Anda. |
Untuk menguji data lain, seperti file DataStore, tulis tindakan perangkat.
Mode transportasi
Setiap cadangan menggunakan nilai BackupTransportMode. Tabel berikut mencantumkan mode transportasi yang didukung:
| Mode | Skenario |
|---|---|
LOCAL |
Pencadangan ke transportasi pencadangan lokal perangkat. Data cadangan tetap berada di perangkat. |
CLOUD_ENCRYPTED |
Cadangan cloud, seperti yang disimpan ke Akun Google pengguna dari perangkat dengan enkripsi sisi klien. |
CLOUD_UNENCRYPTED |
Cadangan cloud dari perangkat yang tidak memiliki enkripsi sisi klien. |
DEVICE_TO_DEVICE |
Transfer antarperangkat, misalnya melalui kabel atau Wi-Fi. |
LOCAL berfungsi di perangkat yang didukung. Mode cloud dan perangkat-ke-perangkat
membuat cadangan yang sama seperti tindakan Backup data aplikasi di Android Studio:
cadangan disimpan ke mesin pengembangan Anda, bukan diupload atau
dikirim ke perangkat lain. Mode cloud dan perangkat ke perangkat memerlukan layanan Google Play di perangkat dan build aplikasi yang dapat di-debug. Jika aturan pencadangan Anda bergantung pada enkripsi sisi klien, uji kedua mode cloud.
Persyaratan
Untuk menggunakan pengujian pencadangan dan pemulihan otomatis, pastikan lingkungan Anda memenuhi persyaratan berikut:
- Gradle versi 9.6.0 atau yang lebih tinggi.
- JDK versi 17 atau yang lebih baru.
- Perangkat atau emulator yang menjalankan Android 12 (level API 31) atau yang lebih tinggi yang terhubung melalui adb. Untuk menguji transfer antara dua perangkat, hubungkan dua perangkat.
Kompatibilitas
Tabel berikut mencantumkan versi minimum dan yang direkomendasikan untuk pengujian pencadangan dan pemulihan otomatis:
| Komponen | Versi minimum | Versi yang direkomendasikan |
|---|---|---|
| Android Studio | Rabbit 2 Canary 1 | Rabbit 2 Canary 4 atau yang lebih tinggi |
| Plugin Android Gradle (AGP) | 9.5.0-alpha06 | 9.5.0-alpha09 atau yang lebih tinggi |
androidx.test.backup library |
1.0.0-alpha01 | 1.0.0-alpha02 atau yang lebih tinggi |
Setiap versi pratinjau AGP memerlukan versi pratinjau Android Studio yang cocok. Untuk mengetahui informasi selengkapnya, lihat pratinjau AGP.
Menyiapkan project
Untuk menambahkan suite pengujian pencadangan dan pemulihan ke modul aplikasi, selesaikan langkah-langkah berikut:
Mengaktifkan tanda eksperimental
Dalam file gradle.properties project Anda, aktifkan suite pengujian dan pengujian
cadangan:
android.experimental.testSuiteSupport=true
android.experimental.enableBackupTest=true
Mendeklarasikan suite pengujian
Dalam file build.gradle.kts modul aplikasi, deklarasikan suite pengujian pencadangan di
blok testOptions:
android {
testOptions {
backupTests {
create("backupTest")
}
}
}
Nama rangkaian pengujian, backupTest dalam contoh ini, menentukan direktori set sumber
dan nama tugas Gradle. Anda dapat menyesuaikan suite dengan
properti opsional berikut:
- Gunakan
backupTestLibraryVersionuntuk menetapkan versi libraryandroidx.test.backupyang ditambahkan AGP ke suite, seperti"1.0.0-alpha02". Properti ini bersifat opsional. Jika Anda menghilangkannya, AGP akan melakukan penggantian ke versi library default berdasarkan versi AGP. - Gunakan
targetVariantsuntuk menentukan varian build yang diuji suite, sepertitargetVariants.add("demoDebug"). Properti ini bersifat opsional. Jika Anda menghilangkannya, suite akan menguji variantestBuildTypemodul Anda, yang secara default adalahdebug. Sebaiknya uji varian yang dapat di-debug karena mode transportasi cloud dan perangkat ke perangkat memerlukan aplikasi yang dapat di-debug.
AGP menambahkan library host, androidx.test.backup:backup-host, dan JUnit 5 ke
pengujian host. Tindakan ini akan menambahkan library perangkat, androidx.test.backup:backup, ke
APK pengujian dan menetapkan runner instrumentasi APK pengujian.
Untuk menambahkan dependensi lain, gunakan blok hostJar dan testApk dari suite.
Misalnya, untuk menggunakan Truth dalam pengujian host, tambahkan ke blok hostJar:
create("backupTest") {
hostJar {
dependencies {
implementation("com.google.truth:truth:1.4.2")
}
}
}
Membuat set sumber
Buat direktori set sumber yang cocok dengan nama rangkaian pengujian:
- Letakkan pengujian host di
src/{suiteName}/test/kotlin/. - Tempatkan tindakan perangkat di
src/{suiteName}/androidTest/kotlin/.
Misalnya, rangkaian pengujian bernama backupTest menggunakan struktur direktori berikut:
app/src/backupTest/
├── androidTest/
│ └── kotlin/com/example/myapp/ThemeActions.kt
└── test/
└── kotlin/com/example/myapp/SettingsBackupTest.kt
Memeriksa konfigurasi cadangan Anda
Pengujian mencadangkan dan memulihkan data yang disertakan dalam konfigurasi pencadangan aplikasi Anda. Pastikan pencadangan diaktifkan untuk aplikasi Anda dan aturan pencadangan menyertakan data yang Anda uji.
Menulis pengujian pencadangan dan pemulihan
Tulis pengujian host di set sumber test suite. Setiap class pengujian memerlukan dua anotasi:
@ExtendWith(BackupRestoreExtension::class)menyediakan perangkat terhubung ke pengujian.@BackupRestoreConfig(applicationId = "...")menetapkan ID aplikasi aplikasi yang sedang diuji. Gunakan ID aplikasi varian yang Anda uji, termasukapplicationIdSuffix.
Setiap metode pengujian mendeklarasikan parameter BackupRestoreController untuk setiap perangkat
yang digunakannya.
Menguji jenis penyimpanan bawaan
Untuk menguji jenis penyimpanan bawaan, panggil runBackupRestoreFlow dengan
data yang akan di-seed, direktori untuk file cadangan, dan mode transpor. Metode
menjalankan langkah-langkah berikut di perangkat:
- Menyemai setiap domain penyimpanan.
- Menghentikan aplikasi.
- Mencadangkan aplikasi.
- Menghapus data aplikasi.
- Memulihkan cadangan.
- Memverifikasi bahwa setiap domain penyimpanan memiliki nilai awal.
Jika langkah gagal, metode akan menampilkan pengecualian, yang menyebabkan pengujian gagal. Contoh
berikut menguji kunci SharedPreferences, baris SQLite, dan file internal menggunakan transportasi cadangan lokal:
package com.example.myapp
import androidx.test.backup.host.BackupRestoreConfig
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)
@BackupRestoreConfig(applicationId = "com.example.myapp")
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,
)
}
}
Perhatikan hal-hal berikut saat Anda menentukan domain penyimpanan:
Preferencemengambil nama fileSharedPreferencestanpa ekstensi.xml.TextFiledanBinaryFilemenyelesaikan jalur relatif terhadapfilesDiraplikasi Anda.Databasemenulis baris dengan SQL, dan membuat tabel dengan kolomTEXTjika tabel tidak ada. Jika aplikasi Anda menggunakan Room, sebaiknya uji konten database dengan tindakan perangkat yang menggunakan DAO Anda sehingga database memiliki skema yang diharapkan Room.
Menulis tindakan perangkat
Untuk menyisipkan atau memverifikasi data yang tidak tercakup dalam jenis penyimpanan bawaan, tulis
tindakan perangkat di set sumber androidTest suite. Tindakan perangkat
mengimplementasikan BackupDeviceAction dan menggantikan dua anggota:
- Tetapkan
phasekeBackupDeviceAction.PHASE_POPULATEuntuk tindakan yang menyemai data, atauBackupDeviceAction.PHASE_VERIFYuntuk tindakan yang memeriksa data. - Terapkan
executeuntuk menjalankan tindakan denganContextaplikasi Anda dan argumen dari pengujian host. Menampilkan hasil yang dibuat dengan metode factoryBackupDeviceActionResult.successatauBackupDeviceActionResult.failure, dan meneruskan pesan error kefailure.
Metode execute berjalan dalam proses aplikasi Anda di thread latar belakang, sehingga
dapat memblokir hingga operasi baca dan tulis selesai.
Contoh berikut menyemai dan memverifikasi setelan tema yang disimpan aplikasi dengan Preferences DataStore. Aplikasi menentukan DataStore di set sumber utamanya:
// 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")
Tindakan perangkat menggunakan definisi yang sama:
// 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")
}
}
}
Untuk menampilkan data ke pengujian host, teruskan peta ke metode success. Pengujian host
membaca peta dari BackupActionResult.Success.data.
Menjalankan setiap langkah alur
Untuk menggunakan tindakan perangkat, atau untuk menyesuaikan alur, panggil setiap langkah sendiri. Pengujian host berikut menjalankan tindakan perangkat dari bagian sebelumnya seputar pencadangan dan pemulihan cloud:
package com.example.myapp
import androidx.test.backup.host.BackupActionResult
import androidx.test.backup.host.BackupRestoreConfig
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)
@BackupRestoreConfig(applicationId = "com.example.myapp")
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)
}
Jika tindakan perangkat gagal, runOnDevice akan menampilkan
BackupActionResult.Failure, bukan menampilkan pengecualian,
jadi periksa hasilnya. Langkah lainnya akan menampilkan pengecualian jika gagal.
Menguji transfer antar-perangkat
Untuk menguji transfer antara dua perangkat, deklarasikan parameter BackupRestoreController
untuk setiap perangkat. Cadangkan aplikasi di perangkat sumber, lalu
pulihkan file cadangan di perangkat target. Contoh berikut menggunakan kembali
konstanta dan helper assertSuccess dari contoh sebelumnya:
package com.example.myapp
import androidx.test.backup.host.BackupRestoreConfig
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)
@BackupRestoreConfig(applicationId = "com.example.myapp")
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()
}
}
Nilai role dari @Device adalah label yang membuat
pengujian lebih mudah dibaca. Hal ini tidak memengaruhi perangkat yang mendapatkan parameter. Jika hanya
satu perangkat yang terhubung, kedua parameter akan menggunakan perangkat tersebut, dan pengujian
akan menyimulasikan transfer pada satu perangkat. Menghapus data target akan memastikan bahwa data yang dipulihkan berasal dari cadangan.
Pilih perangkat
Secara default, framework pengujian mengurutkan perangkat online berdasarkan nomor seri dan
menetapkannya ke parameter BackupRestoreController berdasarkan posisi. Misalnya,
parameter pertama metode pengujian mendapatkan perangkat pertama, dan parameter
kedua mendapatkan perangkat kedua. Jika tidak ada perangkat untuk suatu posisi, parameter akan mendapatkan perangkat pertama. Posisi menghitung semua parameter metode,
jadi deklarasikan parameter perangkat sebelum parameter lainnya, seperti @TempDir. Variabel lingkungan ANDROID_SERIAL tidak memengaruhi penetapan perangkat.
Untuk memilih perangkat, tetapkan nilai serial atau api dari @Device, atau tetapkan properti sistem JVM. Properti sistem lebih diutamakan daripada nilai @Device. Tabel
berikut mencantumkan properti sistem yang didukung:
| Properti sistem | Deskripsi |
|---|---|
androidx.test.backup.device.serial |
Nomor seri perangkat untuk semua parameter perangkat. |
androidx.test.backup.device.serials |
Daftar nomor seri yang dipisahkan koma, yang ditetapkan ke parameter berdasarkan posisi. |
androidx.test.backup.device.serial.{name} |
Nomor seri untuk parameter yang dianotasi dengan @Device(serial = "{name}"). |
androidx.test.backup.device.api |
Level API perangkat untuk semua parameter perangkat. |
androidx.test.backup.device.apis |
Daftar level API yang dipisahkan koma, yang ditetapkan ke parameter berdasarkan posisi. |
Teruskan properti ini dengan -D saat Anda menjalankan pengujian dari command line.
Properti berlaku untuk setiap pengujian yang berjalan, jadi gunakan opsi --tests untuk
hanya menjalankan pengujian yang memerlukannya. Misalnya, perintah berikut menjalankan
ThemeTransferTest dengan emulator-5554 sebagai perangkat sumber dan
emulator-5556 sebagai perangkat target:
./gradlew :app:testDebugBackupTestDefaultTestSuite --tests "com.example.myapp.ThemeTransferTest" -Dandroidx.test.backup.device.serials=emulator-5554,emulator-5556Menyimpan data aplikasi di antara pengujian
Sebelum setiap pengujian, framework pengujian akan menghapus data aplikasi Anda di setiap perangkat yang
digunakan pengujian. Untuk menonaktifkan ini untuk class atau metode pengujian, anotasikan dengan
@Isolation(IsolationPolicy.MANUAL).
Menulis pengujian di Java
Metode BackupRestoreController adalah fungsi penangguhan Kotlin. Jika Anda menulis
pengujian host di Java, gunakan varian Async dari setiap metode, seperti
runBackupRestoreFlowAsync, yang menampilkan ListenableFuture.
Menjalankan pengujian pencadangan dan pemulihan
AGP membuat tugas Gradle untuk setiap varian yang diuji rangkaian, yang diberi nama
test{Variant}{SuiteName}DefaultTestSuite. Untuk menjalankan suite backupTest
terhadap varian debug, hubungkan perangkat Anda dan jalankan tugas:
./gradlew :app:testDebugBackupTestDefaultTestSuiteTugas check, test, dan connectedCheck tidak menjalankan suite pengujian pencadangan dan pemulihan, jadi jalankan tugas suite secara eksplisit.
Untuk menjalankan satu class atau metode pengujian, gunakan
opsi --tests:
./gradlew :app:testDebugBackupTestDefaultTestSuite --tests "com.example.myapp.ThemeBackupTest"Memeriksa laporan pengujian
Setelah menjalankan pengujian selesai, AGP akan membuat laporan pengujian HTML di
{module}/build/reports/tests/{taskName}/index.html, seperti
app/build/reports/tests/testDebugBackupTestDefaultTestSuite/index.html.
Masalah umum dan batasan
Pengujian pencadangan dan pemulihan otomatis memiliki masalah umum dan batasan berikut:
- Dukungan Android Studio untuk suite pengujian belum tersedia. Editor kode mungkin menampilkan error referensi yang belum terselesaikan dalam file sumber suite pengujian, meskipun project Anda dikonfigurasi dengan benar. Bangun dan jalankan pengujian pencadangan dan pemulihan dari command line menggunakan Gradle.
- Pengujian hanya berjalan di perangkat dan emulator yang terhubung ke komputer pengembangan Anda melalui adb. Firebase Test Lab tidak didukung.
- Pengujian menghapus data aplikasi Anda dan mengubah setelan pencadangan di perangkat yang digunakan. Sebaiknya gunakan perangkat pengujian khusus.
Untuk mengetahui perubahan di setiap rilis, lihat catatan rilis pengujian pencadangan dan pemulihan.