AGP 9.5のバックアップと復元の自動テストを試した|バックアップ規則の書き間違いを見つける設定と、つまずきやすい点

AGP 9.5で、アプリのバックアップと復元をテストで確かめられるようになった

Androidは、ユーザーが新しい端末へ移ったときやアプリを入れ直したときに、Googleアカウントへのバックアップや端末間の移行から、アプリのデータを復元します。どのデータを含めるかはアプリの「バックアップ規則」(data_extraction_rules.xml など)で決まります。規則で大事なデータまで外してしまっても、外すはずのデータを含めたままにしても、ユーザーには何のエラーも出ません。設定やメモが消えていることにユーザーが気づくのは、新しい端末への移行が終わったあとです。

Googleが10月28日(日本時間)に公開したAndroid Gradle plugin(AGP)9.5には、この確認を自動化する試験提供の機能「Automated backup and restore testing」が入っています。パソコン側で動くJUnit 5のテストが、つないだ端末やエミュレーターにデータを書き込み、バックアップ、データの消去、復元を行ってから、データが元どおりかを確かめます。11月6日(日本時間)にはパッチ版の9.5.1が出ています。試験提供の間は、APIや設定の書き方が版ごとに変わることがあると公式の文書は書いています。

この記事では、AGP 9.5.1とGradle 9.9.0で最小のアプリを作り、手元のMacのエミュレーター(Android 15、API 35)で試しました。設定の手順、規則の書き間違いをテストがどう知らせるか、つまずきやすい点をまとめます。

記事の要点

バックアップ規則の「外しすぎ」と「外し忘れ」を、どちらもテストの失敗として見つけられた

2026年11月9日、AGP 9.5.1、Gradle 9.9.0、API 35のエミュレーターで確かめた結果です。

確かめられること

復元後に、設定・DB・ファイルが残っているか

復元してほしくないデータ(ログイン用のトークンなど)が復元されないことも確かめられます。

用意するもの

Gradle 9.6.0以上、JDK 17以上、Android 12以上の端末

端末はadbでつなぎます。API 36.1以上の端末では、Google Play開発者サービス26.33.30以上も要ります。gradle.properties に試験機能のフラグを2つ足します。

見つけた書き間違い

外しすぎも外し忘れも、食い違ったファイルのパスを示して失敗

下書きのファイル(drafts/)まで規則で外すと「File not found」、トークンを外し忘れると「to be absent, but found it」で失敗しました。

つまずきやすい点

テスト関数の : Unit を書き忘れると、黙って実行されない

ほかのテストと同じクラスにあると、結果は「成功」のままでした。

テストが端末で行う6つの手順と、確かめられるデータ

テストの中心は runBackupRestoreFlow という関数です。確かめたいデータを並べて渡すと、つないだ端末で次の6つの手順を順に行い、どこかで食い違えば例外を投げてテストを失敗させます。

runBackupRestoreFlow が端末で行うこと

1

データを書き込む

2

アプリを止める

3

バックアップする

4

アプリのデータを消す

5

復元する

6

書いた値があるか、外したデータがないかを確かめる

出典: Android DevelopersのAutomated backup and restore testing(2026年11月9日閲覧)。

書き込むデータの種類は、あらかじめ用意されたものから選べます。SharedPreferences のキー(Preference)、Preferences DataStoreのキー(DataStorePreference)、SQLiteの行(Database)、アプリ内のテキストファイルとバイナリファイル(TextFile、BinaryFile)の5つです。ExcludedData で包んだデータは、「復元されないこと」を確かめる対象になります。RoomのデータベースやProto DataStoreは、端末側で動くコード(デバイスアクション)を自分で書いて確かめます。

バックアップの方式は4つから選びます。端末の中だけで完結する LOCAL は、要点カードの「用意するもの」の条件を満たす端末ならどれでも使えます。クラウドへのバックアップを再現する CLOUD_ENCRYPTED・CLOUD_UNENCRYPTED と、端末間の移行を再現する DEVICE_TO_DEVICE には、端末のGoogle Play開発者サービスと、テストするアプリのデバッグ用のビルド(debuggable)が要ります。この記事の結果は、断りのない限り LOCAL で確かめたものです。

gradle.propertiesとbuild.gradle.ktsに足す設定

まず、プロジェクトの gradle.properties に試験機能のフラグを2つ足します。手元のサンプルの gradle.properties 全体です。

org.gradle.jvmargs=-Xmx2g
android.useAndroidX=true
android.experimental.testSuiteSupport=true
android.experimental.enableBackupTest=true

次に、アプリのモジュールの build.gradle.kts で、testOptions にバックアップのテストを宣言します。backupTest はテストのまとまりの名前で、ソースのディレクトリ名とGradleのタスク名に使われます。

// app/build.gradle.kts(プロジェクト直下の build.gradle.kts で AGP 9.5.1 を指定)
android {
    // ...
    testOptions {
        backupTests {
            create("backupTest") {
                backupTestLibraryVersion = "1.0.0-alpha04"
            }
        }
    }
}

テストのコードは、この名前に合わせた src/backupTest/test/kotlin/ の下に置きます。

app/src/
├── main/
│   ├── AndroidManifest.xml          # android:dataExtractionRules などで規則を指定
│   └── res/xml/data_extraction_rules.xml
└── backupTest/
    └── test/kotlin/com/example/backupsample/SettingsBackupTest.kt

アプリの設定・データベース・ファイルを確かめるテストのコード

手元のサンプルでは、テーマの設定、メモのデータベースの行、下書きのテキストファイルの3つが復元されることと、ログイン用のトークンを入れたファイルが復元されないことを、1つのテストで確かめました。このテストは、Android Developersの文書の例(コードはApache License 2.0)を元に、値を変え(一部は日本語に)、Preferences DataStoreの項目を除いたものです。outputDir は、バックアップのファイルを置く一時ディレクトリ(JUnit 5の @TempDir)です。

package com.example.backupsample

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 "買い物"),
                ),
                StorageDomain.TextFile(path = "drafts/note.txt", content = "牛乳を買う"),
                // バックアップ規則で外した auth/ のファイルは、復元されないことを確かめる
                StorageDomain.ExcludedData(
                    StorageDomain.TextFile(path = "auth/session.txt", content = "temp-token")
                ),
            ),
            outputDir = outputDir,
            mode = BackupTransportMode.LOCAL,
        )
    }
}

関数の宣言にある : Unit は省けません。理由は後半の「つまずきやすい点」で説明します。バックアップ規則は、トークンを入れた auth/ だけを外しています。

<?xml version="1.0" encoding="utf-8"?>
<!-- Android 12 以降のクラウドバックアップと端末間の移行 -->
<data-extraction-rules>
    <cloud-backup>
        <exclude domain="file" path="auth/" />
    </cloud-backup>
    <device-transfer>
        <exclude domain="file" path="auth/" />
    </device-transfer>
</data-extraction-rules>

テストは、エミュレーターを起動してadbでつないだ状態で、Gradleのタスクを指定して実行します。check、test、connectedCheck のタスクではこのテストは動かないので、タスク名をそのまま指定します。

# エミュレーターを起動して adb でつないだ状態で、Gradle から実行する
./gradlew :app:testDebugBackupTestDefaultTestSuite

# 結果のレポート
app/build/reports/tests/testDebugBackupTestDefaultTestSuite/index.html

規則を書き間違えると、食い違ったファイルのパスを示してテストが失敗した

上の規則では、テストは合格しました(11月9日の実行でBUILD SUCCESSFUL、36秒)。続けて、規則をわざと2通りに書き間違えて、同じテストを実行しました。

バックアップ規則と、テストの結果

正しい規則

auth/ だけを外す

合格

外しすぎ

下書きの drafts/ まで外した

失敗: 復元後に drafts/note.txt が見つからない

外し忘れ

auth/ を外し忘れた

失敗: 復元後に auth/session.txt が残っている

2026年11月8日と9日に、API 35のエミュレーターで LOCAL の方式で実行。書き間違いは <cloud-backup> の中で行いました。

どちらの失敗も、Gradleの出力にどのデータで食い違ったかが出ます。

# drafts/ まで外してしまった場合
SettingsBackupTest > settingsSurviveBackupAndRestore(BackupRestoreController, Path) FAILED
    java.io.IOException: AssertStorageAction failed for TextFile(path='drafts/note.txt', content='牛乳を買う'): File not found at path: /data/user/0/com.example.backupsample/files/drafts/note.txt

# auth/ を外し忘れた場合
SettingsBackupTest > settingsSurviveBackupAndRestore(BackupRestoreController, Path) FAILED
    java.io.IOException: AssertStorageAction failed for ExcludedData(TextFile(path='auth/session.txt', content='temp-token')): Expected file /data/user/0/com.example.backupsample/files/auth/session.txt to be absent, but found it.

HTMLのレポートにも、同じメッセージが失敗の詳細として表示されます。

下書きのフォルダーまで規則で外したときの、テストレポートの失敗の詳細の画面。Gradle Test Run :app:testDebugBackupTestDefaultTestSuite の settingsSurviveBackupAndRestore で、tests 1、failures 1、skipped 0、successful 0%。Failure details に「java.io.IOException: AssertStorageAction failed for TextFile(path='drafts/note.txt', content='牛乳を買う'): File not found at path: /data/u…」と表示されている。
下書きのフォルダーまで規則で外したときのテストレポート(testDebugBackupTestDefaultTestSuite の index.html)。1件中1件が失敗し、失敗の詳細に、復元後に見つからなかったファイルのパスが出ています。

手元では、LOCALの方式がcloud-backupの規則に従った

Android 12以降の規則には、クラウドへのバックアップ用の <cloud-backup> と、端末間の移行用の <device-transfer> の2つの区分があります。drafts/ を片方だけで外して試すと、<device-transfer> だけで外したときは合格し、<cloud-backup> だけで外したときは失敗しました(11月8日と9日に同じ結果)。手元のAPI 35のエミュレーターでは、LOCAL の方式は <cloud-backup> の規則に従いました。端末側の暗号化を条件にした規則(requireFlags="clientSideEncryption" や disableIfNoEncryptionCapabilities)を使うときは、2つのクラウドの方式の両方でテストするよう公式の文書は勧めています。端末間の移行の規則を確かめる方式は DEVICE_TO_DEVICE です(この記事では試していません)。

つまずきやすい点

テスト関数の : Unit

= runBlocking { ... } の形の関数は、ブロックの最後の式の値を返します。このテストのように最後の式が値を返すと、: Unit を省いたときに戻り値の型が Unit 以外になり、JUnit 5はその関数をテストとして扱いません。テストがそれ1つなら「did not discover any tests」で失敗しますが、ほかのテストと同じクラスにあると、黙って外れてBUILD SUCCESSFULになりました。

Gradleの版

AGP 9.5はGradle 9.6.0以上が必要です。Gradle 9.2.1では「Minimum supported Gradle version is 9.6.0」と出て、設定の段階で止まりました。

試験機能のフラグ

android.experimental.enableBackupTest=true がないと、足すように求めるメッセージで止まります。

実行する場所

Android Studioはまだこのテストに対応しておらず、エディターでは参照エラーが表示される場合があります。実行はコマンドラインのGradleで行います。Firebase Test Labでは動きません。

テスト用の端末

テストのたびにアプリのデータを消し、端末のバックアップの設定も変えます。普段使いの端末とは分け、テスト専用の端末やエミュレーターを使います。

クラウドの方式

手元のAPI 35のエミュレーター(Google Play開発者サービス24.23、アプリはデバッグ用のビルド)では、CLOUD_ENCRYPTED がGoogle Play開発者サービスのファイルを読めない「Permission Denial」のエラーで失敗し、同じテストは LOCAL で合格しました(11月8日)。

「実行する場所」と「テスト用の端末」は公式の文書の既知の制限、ほかは2026年11月8日・9日の手元の実行結果です(: Unit は両日、Gradleの版・フラグ・クラウドの方式は8日)。

このうち : Unit の書き忘れは、テストの数が増えたあとに見落としやすい点です。手元では、: Unit のあるテストと書き忘れたテストを同じクラスに置くと、レポートのテスト数は1件で、書き忘れたテストは一覧にも出ませんでした。新しくテストを足したら、レポートのテスト数が増えたかを確かめます。また、このテストが確かめるのはテストに並べたデータだけなので、規則を変えたときは、守りたいデータと外したいデータがテストに並んでいるかもあわせて見直します。

参考資料

検証環境

  • macOS 27.0.1(Apple Silicon)、JDK 17.0.16
  • AGP 9.5.1、Gradle 9.9.0、androidx.test.backup 1.0.0-alpha04
  • Android Emulator 36.3.10(Android 15、API 35)、Google Play開発者サービス 24.23.35
  • 検証日: 2026年11月9日(一部は11月8日)

この記事は、AIが調査と下書きを担当し、上記の環境で実際に動かして結果を確かめ、下書きとは別の担当が公開前に本文と出典を確認しています。制作と訂正の方針は編集・訂正方針で公開しています。