Kullanıcılar yeni bir cihaz kurduğunda veya uygulamanızı yeniden yüklediğinde Android, uygulamanızın verilerini bulut yedeğinden ya da cihazlar arası aktarımla geri yükleyebilir. Yedekleme kurallarınız önemli verileri dışarıda bırakıyorsa veya uygulamanız geri yüklediği verileri kullanamıyorsa kullanıcılar hata görmeden ayarlarını ve ilerleme durumlarını kaybeder.
Otomatik yedekleme ve geri yükleme testleri, uygulamanızı yayınlamadan önce bu sorunları tespit etmenize yardımcı olur. Bu testler, geliştirme makinenizde çalışan ve bağlı cihazları Android Debug Bridge (adb) aracılığıyla kontrol eden JUnit 5 testleridir. Her test, cihazdaki uygulama verilerini başlatır, Android yedekleme çerçevesi aracılığıyla uygulamayı yedekleyip geri yükler ve ardından verilerin bozulmadan kaldığını kontrol eder. Testleri, modülünüzün derleme dosyasında Android Gradle eklentisi (AGP) test paketi olarak yapılandırırsınız.
İşleyiş şekli
Yedekleme ve geri yükleme test paketinin birlikte çalışan iki bölümü vardır:
- Ana makine testleri, geliştirme makinenizde çalışan JUnit 5 testleridir. Verileri beslemek, uygulamanızı yedekleyip geri yüklemek ve sonuçları kontrol etmek için her cihazda bir
BackupRestoreControllerkullanırlar. - Cihaz işlemleri,
BackupDeviceActionsınıfını uygulayan sınıflardır. Cihazdaki uygulamanızın sürecinde çalıştıkları için uygulamanızın kendi kodu aracılığıyla verileri okuyup yazabilirler. Ana makine, sınıf adına göre cihaz işlemlerini test eder.
Paketi çalıştırdığınızda AGP, uygulamanızı ve cihaz işlemlerinizi içeren bir test APK'sı oluşturur. Test çerçevesi, her testten önce her iki APK'yı da testin kullandığı her cihaza yükler ve ardından uygulamanızın verilerini temizler.
Yerleşik depolama türleri
Aşağıdaki tabloda, cihaz işlemleri yazmadan veri eklemenize ve doğrulama verisi doğrulamanıza olanak tanıyan StorageDomain alt sınıfları listelenmiştir:
| Depolama alanı | Veriler |
|---|---|
StorageDomain.Preference |
SharedPreferences dosyasındaki bir anahtar. |
StorageDomain.Database |
SQLite veritabanı tablosundaki bir satır. |
StorageDomain.TextFile |
Uygulamanızın dahili depolama alanındaki bir metin dosyası. |
StorageDomain.BinaryFile |
Uygulamanızın dahili depolama alanındaki bir ikili program dosyası. |
DataStore dosyaları gibi diğer verileri test etmek için cihaz işlemleri yazın.
Ulaşım modları
Her yedekleme için bir BackupTransportMode değeri kullanılır. Aşağıdaki tabloda desteklenen ulaşım modları listelenmiştir:
| Mod | Senaryo |
|---|---|
LOCAL |
Cihazın yerel yedekleme aktarımına yedekleme. Yedekleme verileri cihazda kalır. |
CLOUD_ENCRYPTED |
İstemci tarafı şifreleme özelliğine sahip bir cihazdan kullanıcının Google Hesabı'na kaydedilen bulut yedeklemesi gibi bir yedekleme. |
CLOUD_UNENCRYPTED |
İstemci tarafı şifreleme özelliği olmayan bir cihazdan alınan bulut yedeği. |
DEVICE_TO_DEVICE |
Cihazdan cihaza aktarım (ör. kablo veya kablosuz ağ üzerinden) |
LOCAL desteklenen tüm cihazlarda çalışır. Bulut ve cihazlar arası modlar, Android Studio'daki Uygulama Verilerini Yedekle işlemiyle aynı yedekleri oluşturur: Yedek, başka bir cihaza yüklenmek veya gönderilmek yerine geliştirme makinenize kaydedilir. Bulut ve cihazlar arası modlar, cihazda Google Play Hizmetleri'nin ve uygulamanızın hata ayıklanabilir bir derlemesinin olmasını gerektirir. Yedekleme kurallarınız istemci tarafı şifrelemeye bağlıysa her iki bulut modunu da test edin.
Şartlar
Otomatik yedekleme ve geri yükleme testini kullanmak için ortamınızın aşağıdaki koşulları karşıladığından emin olun:
- Gradle 9.6.0 veya sonraki bir sürüm.
- JDK 17 veya sonraki sürümler.
- Android 12 (API düzeyi 31) veya sonraki sürümlerin yüklü olduğu ve adb üzerinden bağlanan bir cihaz ya da emülatör. İki cihaz arasındaki aktarımları test etmek için iki cihaz bağlayın.
Uyumluluk
Aşağıdaki tabloda, otomatik yedekleme ve geri yükleme testi için minimum ve önerilen sürümler listelenmiştir:
| Bileşen | Minimum sürüm | Önerilen sürüm |
|---|---|---|
| Android Studio | Rabbit 2 Canary 1 | Rabbit 2 Canary 4 veya daha yeni sürümler |
| Android Gradle eklentisi (AGP) | 9.5.0-alpha06 | 9.5.0-alpha09 veya daha yeni bir sürüm |
androidx.test.backup kitaplık |
1.0.0-alpha01 | 1.0.0-alpha02 veya daha yeni bir sürüm |
AGP'nin her önizleme sürümü, Android Studio'nun eşleşen önizleme sürümünü gerektirir. Daha fazla bilgi için AGP önizlemeleri başlıklı makaleyi inceleyin.
Projenizi oluşturma
Uygulama modülünüze yedekleme ve geri yükleme test paketi eklemek için aşağıdaki adımları tamamlayın:
Deneysel işaretleri etkinleştirme
Projenizin gradle.properties dosyasında test paketlerini ve yedekleme testlerini etkinleştirin:
android.experimental.testSuiteSupport=true
android.experimental.enableBackupTest=true
Test paketini tanımlama
Uygulama modülünüzün build.gradle.kts dosyasında, testOptions bloğunda bir yedekleme test paketi tanımlayın:
android {
testOptions {
backupTests {
create("backupTest")
}
}
}
Bu örnekte backupTest olan paket adı, kaynak grubu dizinlerini ve Gradle görev adlarını belirler. Paketi aşağıdaki isteğe bağlı özelliklerle özelleştirebilirsiniz:
- AGP'nin pakete eklediği
androidx.test.backupkitaplıklarının sürümünü (ör."1.0.0-alpha02") ayarlamak içinbackupTestLibraryVersionkullanın. Bu özellik isteğe bağlıdır. Bu bağımlılığı atlarsanız AGP, AGP sürümüne göre varsayılan bir kitaplık sürümüne geri döner. - Paketin test ettiği derleme değişkenlerini (ör.
targetVariants.add("demoDebug")) belirtmek içintargetVariantskullanın. Bu özellik isteğe bağlıdır. Bu parametreyi atlarsanız paket, modülünüzüntestBuildTypevaryantlarını test eder. Bu varyantlar varsayılan olarakdebugşeklindedir. Bulut ve cihazlar arası aktarım modları hata ayıklanabilir bir uygulama gerektirdiğinden hata ayıklanabilir bir varyantı test etmenizi öneririz.
AGP, ana makine testlerine ana makine kitaplığını, androidx.test.backup:backup-host ve JUnit 5'i ekler. Test APK'sına cihaz kitaplığını androidx.test.backup:backup ekler ve test APK'sının enstrümantasyon çalıştırıcısını ayarlar.
Başka bağımlılıklar eklemek için paketin hostJar ve testApk bloklarını kullanın.
Örneğin, ana makine testlerinizde Truth'u kullanmak için hostJar
bloğuna ekleyin:
create("backupTest") {
hostJar {
dependencies {
implementation("com.google.truth:truth:1.4.2")
}
}
}
Kaynak gruplarını oluşturma
Paket adıyla eşleşen kaynak grubu dizinleri oluşturun:
- Ana makine testlerini
src/{suiteName}/test/kotlin/dizinine yerleştirin. - Cihaz işlemlerini
src/{suiteName}/androidTest/kotlin/içine yerleştirin.
Örneğin, backupTest adlı bir pakette aşağıdaki dizin yapısı kullanılır:
app/src/backupTest/
├── androidTest/
│ └── kotlin/com/example/myapp/ThemeActions.kt
└── test/
└── kotlin/com/example/myapp/SettingsBackupTest.kt
Yedekleme yapılandırmanızı kontrol etme
Testler, uygulamanızın yedekleme yapılandırmasının içerdiği verileri yedekler ve geri yükler. Uygulamanız için yedeklemenin etkinleştirildiğinden ve yedekleme kurallarınızın test ettiğiniz verileri içerdiğinden emin olun.
Yedekleme ve geri yükleme testleri yazma
Paketin test kaynak grubunda ana makine testleri yazın. Her test sınıfının iki ek açıklaması olması gerekir:
@ExtendWith(BackupRestoreExtension::class), testlere bağlı cihazlar sağlar.@BackupRestoreConfig(applicationId = "..."), test edilen uygulamanın uygulama kimliğini ayarlar. Test ettiğiniz varyantın uygulama kimliğini kullanın.applicationIdSuffixdeğerini de ekleyin.
Her test yöntemi, kullandığı her cihaz için bir BackupRestoreController parametresi tanımlar.
Yerleşik depolama alanı türlerini test etme
Yerleşik depolama alanı türlerini test etmek için temel veriler, yedek dosyasının dizini ve ulaşım modu ile runBackupRestoreFlow yöntemini çağırın. Yöntem, cihazda aşağıdaki adımları uygular:
- Her depolama alanını başlatır.
- Uygulamayı durdurur.
- Uygulamayı yedekler.
- Uygulamanın verilerini temizler.
- Yedeği geri yükler.
- Her depolama alanının çekirdek değere sahip olduğunu doğrular.
Bir adım başarısız olursa yöntem bir istisna hatası verir ve bu da testin başarısız olmasına neden olur. Aşağıdaki örnekte, yerel yedekleme aktarımı kullanılarak bir SharedPreferences anahtarı, bir SQLite satırı ve dahili bir dosya test edilmektedir:
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,
)
}
}
Depolama alanlarını tanımlarken aşağıdakileri göz önünde bulundurun:
Preference,.xmluzantısı olmadanSharedPreferencesdosyasının adını alır.TextFileveBinaryFile, göreli yolları uygulamanızınfilesDirdizinine göre çözümler.Database, satırı SQL ile yazar ve tablo yoksaTEXTsütunlu tabloyu oluşturur. Uygulamanızda Room kullanılıyorsa veritabanının Room'un beklediği şemaya sahip olması için veritabanı içeriğini DAO'larınızı kullanan cihaz işlemleriyle test etmenizi öneririz.
Cihaz işlemlerini yazma
Yerleşik depolama alanı türlerinin kapsamadığı verileri eklemek veya doğrulamak için paketin androidTest kaynak grubuna cihaz işlemleri yazın. Bir cihaz işlemi, BackupDeviceAction öğesini uygular ve iki üyeyi geçersiz kılar:
- Veri ekleyen bir işlem için
phasedeğeriniBackupDeviceAction.PHASE_POPULATE, verileri kontrol eden bir işlem için iseBackupDeviceAction.PHASE_VERIFYolarak ayarlayın. execute, uygulamanızınContextve ana makine testindeki bağımsız değişkenlerle işlemi çalıştırmak için uygulanır.BackupDeviceActionResult.successveyaBackupDeviceActionResult.failurefabrika yöntemiyle oluşturulmuş bir sonuç döndürün vefailureyöntemine bir hata mesajı iletin.
execute yöntemi, uygulamanızın sürecinde arka plan iş parçacığında çalışır. Bu nedenle, okuma ve yazma işlemleri tamamlanana kadar engellenebilir.
Aşağıdaki örnek, uygulamanın Preferences DataStore ile depoladığı bir tema ayarını başlatır ve doğrular. Uygulama, DataStore'u ana kaynak grubunda tanımlar:
// 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")
Cihaz işlemleri aynı tanımları kullanır:
// 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")
}
}
}
Verileri ana makine testine döndürmek için success yöntemine bir harita iletin. Ana makine testi, haritayı BackupActionResult.Success.data konumundan okur.
Akışın her adımını çalıştırma
Cihaz işlemlerini kullanmak veya akışı özelleştirmek için her adımı kendiniz çağırın. Aşağıdaki ana makine testi, önceki bölümdeki cihaz işlemlerini bulut yedekleme ve geri yükleme ile ilgili olarak çalıştırır:
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)
}
Bir cihaz işlemi başarısız olduğunda runOnDevice, istisna oluşturmak yerine BackupActionResult.Failure döndürür. Bu nedenle sonucu kontrol edin. Diğer adımlar başarısız olursa istisna oluşturur.
Cihazlar arasında aktarım testi yapma
İki cihaz arasında aktarımı test etmek için her cihaz için bir BackupRestoreController
parametresi bildirin. Uygulamayı kaynak cihazda yedekleyin ve ardından yedek dosyasını hedef cihaza geri yükleyin. Aşağıdaki örnekte, önceki örnekteki sabitler ve assertSuccess yardımcı işlevi yeniden kullanılmaktadır:
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()
}
}
@Device için role değeri, testin daha kolay okunmasını sağlayan bir etikettir. Bu ayar, bir parametrenin hangi cihazı alacağını etkilemez. Yalnızca bir cihaz bağlıysa her iki parametre de bu cihazı kullanır ve test, tek bir cihazda aktarımı simüle eder. Hedefin verilerini temizlemek, geri yüklenen verilerin yedekten gelmesini sağlar.
Cihazları seçin
Test çerçevesi, varsayılan olarak çevrimiçi cihazları seri numarasına göre sıralar ve konumlarına göre BackupRestoreController parametrelerine atar. Örneğin, test yönteminin ilk parametresi ilk cihazı, ikinci parametresi ise ikinci cihazı alır. Bir konum için cihaz yoksa parametre ilk cihazı alır. Konumlar, yöntemin tüm parametrelerini sayar. Bu nedenle, cihaz parametrelerini diğer parametrelerden (ör. @TempDir) önce tanımlayın. ANDROID_SERIAL ortam değişkeni, cihaz atamasını etkilemez.
Cihazları seçmek için @Device'nin serial veya api değerlerini ya da JVM sistem özelliklerini ayarlayın. Sistem özellikleri, @Device değerlerine göre önceliklidir. Aşağıdaki tabloda desteklenen sistem özellikleri listelenmiştir:
| Sistem özelliği | Açıklama |
|---|---|
androidx.test.backup.device.serial |
Tüm cihaz parametreleri için cihazın seri numarası. |
androidx.test.backup.device.serials |
Parametrelere konuma göre atanan, virgülle ayrılmış seri numaraları listesi. |
androidx.test.backup.device.serial.{name} |
@Device(serial = "{name}") ile ek açıklama eklenen parametrelerin seri numarası. |
androidx.test.backup.device.api |
Tüm cihaz parametreleri için cihazın API düzeyi. |
androidx.test.backup.device.apis |
Konuma göre parametrelere atanmış API düzeylerinin virgülle ayrılmış listesi. |
Testleri komut satırından çalıştırırken bu özellikleri -D ile iletin.
Özellikler, çalışan her test için geçerlidir. Bu nedenle, yalnızca bu özelliklere ihtiyaç duyan testleri çalıştırmak için --tests seçeneğini kullanın. Örneğin, aşağıdaki komut ThemeTransferTest uygulamasını kaynak cihaz olarak emulator-5554, hedef cihaz olarak da emulator-5556 ile çalıştırır:
./gradlew :app:testDebugBackupTestDefaultTestSuite --tests "com.example.myapp.ThemeTransferTest" -Dandroidx.test.backup.device.serials=emulator-5554,emulator-5556Testler arasında uygulama verilerini saklama
Test çerçevesi, her testten önce testin kullandığı her cihazda uygulamanızın verilerini temizler. Bu özelliği bir test sınıfı veya yöntemi için devre dışı bırakmak üzere @Isolation(IsolationPolicy.MANUAL) ile açıklama ekleyin.
Java'da test yazma
BackupRestoreController yöntemleri Kotlin askıya alma işlevleridir. Java'da ana makine testleri yazıyorsanız her yöntemin Async varyantını (ör. runBackupRestoreFlowAsync) kullanın. Bu varyant, ListenableFuture döndürür.
Yedekleme ve geri yükleme testleri yapma
AGP, paketin test ettiği her varyant için test{Variant}{SuiteName}DefaultTestSuite adlı bir Gradle görevi oluşturur. backupTest paketini debug varyantında çalıştırmak için cihazlarınızı bağlayın ve şu görevi çalıştırın:
./gradlew :app:testDebugBackupTestDefaultTestSuitecheck, test ve connectedCheck görevleri, yedekleme ve geri yükleme test paketlerini çalıştırmaz. Bu nedenle, paketin görevini açıkça çalıştırın.
Tek bir test sınıfı veya yöntemi çalıştırmak için --tests seçeneğini kullanın:
./gradlew :app:testDebugBackupTestDefaultTestSuite --tests "com.example.myapp.ThemeBackupTest"Test raporlarını inceleme
Test çalıştırması tamamlandıktan sonra AGP, {module}/build/reports/tests/{taskName}/index.html konumunda bir HTML test raporu oluşturur. Örneğin, app/build/reports/tests/testDebugBackupTestDefaultTestSuite/index.html.
Bilinen sorunlar ve sınırlamalar
Otomatik yedekleme ve geri yükleme testinde aşağıdaki bilinen sorunlar ve sınırlamalar vardır:
- Test paketleri için Android Studio desteği henüz kullanılamamaktadır. Kod düzenleyici, projeniz doğru şekilde yapılandırılmış olsa bile test paketi kaynak dosyalarında çözümlenmemiş referans hataları gösterebilir. Bunun yerine Gradle'ı kullanarak komut satırından yedekleme ve geri yükleme testleri oluşturup çalıştırın.
- Testler yalnızca adb aracılığıyla geliştirme makinenize bağlı cihazlarda ve emülatörlerde çalıştırılır. Firebase Test Lab desteklenmiyor.
- Testler, uygulamanızın verilerini temizler ve kullandıkları cihazlardaki yedekleme ayarlarını değiştirir. Özel test cihazları kullanmanızı öneririz.
Her sürümdeki değişiklikler için yedekleme ve geri yükleme testi sürüm notlarına bakın.