Android インストルメンテーション テスト

問題を報告 ソースを表示 Nightly · 7.4 . 7.3 7.2 7.1 7.0 6.5

Bazel を初めて使用する場合は、Terraform を使用した Android のビルド Bazel のチュートリアルをご覧ください。

Android インストルメンテーション テストの並列実行

図 1. Android インストルメンテーション テストの並列実行。

android_instrumentation_test を使用すると、デベロッパーは Android Emulator とデバイスでアプリをテストできます。実際の Android フレームワーク API と Android テスト ライブラリを利用します。

密閉性と再現性を確保するため、Bazel は Android を作成して起動します。 サンドボックス内でエミュレータを使用して、テストを常にクリーンな状態から実行できます。各テストは分離されたエミュレータ インスタンスを取得するため、テスト間で状態を渡すことなくテストを並列実行できます。

Android インストルメンテーション テストの詳細については、Android デベロッパー向けドキュメントをご覧ください。

問題については、GitHub 公開バグトラッカーで報告してください。

仕組み

android_instrumentation_test ターゲットで bazel test を初めて実行すると、Bazel は次の手順を実行します。

  1. テスト APK、テスト対象の APK、およびそれらの推移的依存関係をビルドします
  2. クリーンなエミュレータの状態を作成、起動、キャッシュに保存する
  3. エミュレータを起動します。
  4. APK をインストールします。
  5. Android Test Orchestrator を使用してテストを実行します。
  6. エミュレータをシャットダウンする
  7. 結果を報告する

以降のテスト実行では、Bazel はステップ 2 で作成されたクリーンなキャッシュに保存された状態からエミュレータを起動するため、以前の実行からの状態が残りません。キャッシュ エミュレータの状態によってテスト実行も高速化されます。

前提条件

環境が次の前提条件を満たしていることを確認します。

  • Linux。Ubuntu 16.04、18.04 でテスト済みです。

  • Bazel 0.12.0 以降。bazel info release を実行してバージョンを確認します。

bazel info release

これにより、次のような出力が生成されます。

release 4.1.0

KVM が正しく構成されていることを確認するには、次のコマンドを実行します。

apt-get install cpu-checker && kvm-ok

次のメッセージが表示されたら、構成が正しいことを示します。

INFO: /dev/kvm exists
KVM acceleration can be used

インストールするには、次のコマンドを実行します。

apt-get install xvfb

次のコマンドを実行して、Xvfb が正しくインストールされ、/usr/bin/Xvfb にインストールされていることを確認します。

which Xvfb

次のような出力が表示されます。

/usr/bin/Xvfb
  • 32 ビット ライブラリ。テスト インフラストラクチャで使用されるバイナリの一部は 32 ビットであるため、64 ビットマシンでは 32 ビット バイナリを実行できるようにしてください。Ubuntu の場合は、次の 32 ビット ライブラリをインストールします。
sudo apt-get install libc6:i386 libncurses5:i386 libstdc++6:i386 lib32z1 libbz2-1.0:i386

スタートガイド

android_instrumentation_test の一般的なターゲット依存関係グラフを次に示します。

Android インストルメンテーション テストのターゲット依存関係グラフ

図 2. android_instrumentation_test のターゲット依存関係グラフ。

BUILD ファイル

グラフは、次のような BUILD ファイルに変換されます。

android_instrumentation_test(
    name = "my_test",
    test_app = ":my_test_app",
    target_device = "@android_test_support//tools/android/emulated_devices/generic_phone:android_23_x86",
)

# Test app and library
android_binary(
    name = "my_test_app",
    instruments = ":my_app",
    manifest = "AndroidTestManifest.xml",
    deps = [":my_test_lib"],
    # ...
)

android_library(
    name = "my_test_lib",
    srcs = glob(["javatest/**/*.java"]),
    deps = [
        ":my_app_lib",
        "@maven//:androidx_test_core",
        "@maven//:androidx_test_runner",
        "@maven//:androidx_test_espresso_espresso_core",
    ],
    # ...
)

# Target app and library under test
android_binary(
    name = "my_app",
    manifest = "AndroidManifest.xml",
    deps = [":my_app_lib"],
    # ...
)

android_library(
    name = "my_app_lib",
    srcs = glob(["java/**/*.java"]),
    deps = [
        "@maven//:androidx_appcompat_appcompat",
        "@maven//:androidx_annotation_annotation",
    ]
    # ...
)

ルール android_instrumentation_test の主な属性は次のとおりです。

  • test_app: android_binary ターゲット。このターゲットにはテストコードと UIAutomator にサポートしています選択した android_binary 別のターゲットを参照する instruments 属性を指定するには、ターゲットが必要です android_binary(テスト対象のアプリ)

  • target_device: android_device ターゲット。このターゲットは、 Android Emulator の仕様(Bazel による作成、起動、 テストを実行します。詳しくは、Android デバイスの選択に関するセクションをご覧ください。

テストアプリの AndroidManifest.xml には、<instrumentation> タグを含める必要があります。このタグでは、ターゲット アプリのパッケージの属性と、インストルメンテーション テストランナーの完全修飾クラス名 androidx.test.runner.AndroidJUnitRunner を指定する必要があります。

テストアプリの AndroidTestManifest.xml の例を次に示します。

<?xml version="1.0" encoding="UTF-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:tools="http://schemas.android.com/tools"
          package="com.example.android.app.test"
    android:versionCode="1"
    android:versionName="1.0">

    <instrumentation
        android:name="androidx.test.runner.AndroidJUnitRunner"
        android:targetPackage="com.example.android.app" />

    <uses-sdk
        android:minSdkVersion="16"
        android:targetSdkVersion="27" />

    <application >
       <!-- ... -->
    </application>
</manifest>

WORKSPACE の依存関係

このルールを使用するには、プロジェクトでこれらの外部 リポジトリ:

  • @androidsdk: Android SDK。Android Studio からダウンロードしてください。

  • @android_test_support: テストランナー、エミュレータ ランチャー、 android_device 個のターゲット。最新リリースはこちらで確認できます。

これらの依存関係を有効にするには、WORKSPACE ファイルに次の行を追加します。

# Android SDK
android_sdk_repository(
    name = "androidsdk",
    path = "/path/to/sdk", # or set ANDROID_HOME
)

# Android Test Support
ATS_COMMIT = "$COMMIT_HASH"
http_archive(
    name = "android_test_support",
    strip_prefix = "android-test-%s" % ATS_COMMIT,
    urls = ["https://github.com/android/android-test/archive/%s.tar.gz" % ATS_COMMIT],
)
load("@android_test_support//:repo.bzl", "android_test_repositories")
android_test_repositories()

Maven の依存関係

Google MavenMaven Central などのリポジトリの Maven アーティファクトへの依存関係を管理するには、Maven Resolver(rules_jvm_external など)を使用する必要があります。

このページの残りの部分では、rules_jvm_external を使用して以下を行う方法について説明します。 Maven リポジトリから依存関係を解決して取得します。

android_device ターゲットの選択

android_instrumentation_test.target_device は、実行する Android デバイスを指定します。 テストを実行できますこれらの android_device ターゲットは @android_test_support で定義されています。

たとえば、特定のターゲットのソースをクエリするには、次のコマンドを実行します。

bazel query --output=build @android_test_support//tools/android/emulated_devices/generic_phone:android_23_x86

出力は次のようになります。

# .../external/android_test_support/tools/android/emulated_devices/generic_phone/BUILD:43:1
android_device(
  name = "android_23_x86",
  visibility = ["//visibility:public"],
  tags = ["requires-kvm"],
  generator_name = "generic_phone",
  generator_function = "make_device",
  generator_location = "tools/android/emulated_devices/generic_phone/BUILD:43",
  vertical_resolution = 800,
  horizontal_resolution = 480,
  ram = 2048,
  screen_density = 240,
  cache = 32,
  vm_heap = 256,
  system_image = "@android_test_support//tools/android/emulated_devices/generic_phone:android_23_x86_images",
  default_properties = "@android_test_support//tools/android/emulated_devices/generic_phone:_android_23_x86_props",
)

デバイス ターゲット名は次のテンプレートを使用します。

@android_test_support//tools/android/emulated_devices/device_type:system_api_level_x86_qemu2

android_device を起動するには、選択した API レベルの system_image が必要です。システム イメージをダウンロードするには、Android SDK の tools/bin/sdkmanager。たとえば、generic_phone:android_23_x86 のシステム イメージをダウンロードするには、$sdk/tools/bin/sdkmanager "system-images;android-23;default;x86" を実行します。

サポートされている android_device ターゲットの完全なリストを確認するには、 @android_test_support の場合は、次のコマンドを実行します。

bazel query 'filter("x86_qemu2$", kind(android_device, @android_test_support//tools/android/emulated_devices/...:*))'

Bazel は現在、x86 ベースのエミュレータのみをサポートしています。パフォーマンスを向上させるには、 目標は QEMU 個から QEMU2 android_device 個になりました。

テストの実行

テストを実行するには、プロジェクトの project root:/.bazelrc ファイルに次の行を追加します。

# Configurations for testing with Bazel
# Select a configuration by running
# `bazel test //my:target --config={headless, gui, local_device}`

# Headless instrumentation tests (No GUI)
test:headless --test_arg=--enable_display=false

# Graphical instrumentation tests. Ensure that $DISPLAY is set.
test:gui --test_env=DISPLAY
test:gui --test_arg=--enable_display=true

# Testing with a local emulator or device. Ensure that `adb devices` lists the
# device.
# Run tests serially.
test:local_device --test_strategy=exclusive
# Use the local device broker type, as opposed to WRAPPED_EMULATOR.
test:local_device --test_arg=--device_broker_type=LOCAL_ADB_SERVER
# Uncomment and set $device_id if there is more than one connected device.
# test:local_device --test_arg=--device_serial_number=$device_id

次に、いずれかの構成を使用してテストを実行します。

  • bazel test //my/test:target --config=gui
  • bazel test //my/test:target --config=headless
  • bazel test //my/test:target --config=local_device

使用する構成は 1 つだけにしましょう。そうしないと、テストは失敗します。

ヘッドレス テスト

Xvfb を使用すると、グラフィカル インターフェースのないエミュレータでテストできます。これはヘッドレス テストとも呼ばれます。グラフィカル インターフェースを無効にするには テストを実行するときは、テスト引数 --enable_display=false を Bazel に渡します。

bazel test //my/test:target --test_arg=--enable_display=false

GUI テスト

$DISPLAY 環境変数が設定されている場合は、テストの実行中にエミュレータのグラフィカル インターフェースを有効にできます。これを行うには、次のテスト引数を Bazel に渡します。

bazel test //my/test:target --test_arg=--enable_display=true --test_env=DISPLAY

ローカル エミュレータまたはデバイスでのテスト

Bazel は、ローカルで起動されたエミュレータまたは接続されたデバイスで直接テストすることもサポートしています。フラグ --test_strategy=exclusive--test_arg=--device_broker_type=LOCAL_ADB_SERVER を渡して、ローカルテストモードを有効にします。接続されているデバイスが複数ある場合は、フラグ --test_arg=--device_serial_number=$device_id を渡します。ここで、$device_idadb devices にリストされているデバイス / エミュレータの ID です。

サンプル プロジェクト

標準的なプロジェクト サンプルをお探しの場合は、Espresso と UIAutomator を使用するプロジェクトの Android テストサンプルをご覧ください。

Espresso のセットアップ

Espresso を使用して UI テストを作成する場合 (androidx.test.espresso)では、次のスニペットを使用して、 よく使用される Espresso アーティファクトとそのアーティファクトのリストが表示された Bazel ワークスペース dependencies:

androidx.test.espresso:espresso-core
androidx.test:rules
androidx.test:runner
javax.inject:javax.inject
org.hamcrest:java-hamcrest
junit:junit

これらの依存関係を整理する方法の 1 つは、project root/BUILD.bazel ファイルに //:test_deps 共有ライブラリを作成することです。

java_library(
    name = "test_deps",
    visibility = ["//visibility:public"],
    exports = [
        "@maven//:androidx_test_espresso_espresso_core",
        "@maven//:androidx_test_rules",
        "@maven//:androidx_test_runner",
        "@maven//:javax_inject_javax_inject"
        "@maven//:org_hamcrest_java_hamcrest",
        "@maven//:junit_junit",
    ],
)

次に、project root/WORKSPACE に必要な依存関係を追加します。

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

RULES_JVM_EXTERNAL_TAG = "2.8"
RULES_JVM_EXTERNAL_SHA = "79c9850690d7614ecdb72d68394f994fef7534b292c4867ce5e7dec0aa7bdfad"

http_archive(
    name = "rules_jvm_external",
    strip_prefix = "rules_jvm_external-%s" % RULES_JVM_EXTERNAL_TAG,
    sha256 = RULES_JVM_EXTERNAL_SHA,
    url = "https://github.com/bazelbuild/rules_jvm_external/archive/%s.zip" % RULES_JVM_EXTERNAL_TAG,
)

load("@rules_jvm_external//:defs.bzl", "maven_install")

maven_install(
    artifacts = [
        "junit:junit:4.12",
        "javax.inject:javax.inject:1",
        "org.hamcrest:java-hamcrest:2.0.0.0"
        "androidx.test.espresso:espresso-core:3.1.1",
        "androidx.test:rules:aar:1.1.1",
        "androidx.test:runner:aar:1.1.1",
    ],
    repositories = [
        "https://maven.google.com",
        "https://repo1.maven.org/maven2",
    ],
)

最後に、テスト用の android_binary ターゲットに //:test_deps を追加します。 :

android_binary(
    name = "my_test_app",
    instruments = "//path/to:app",
    deps = [
        "//:test_deps",
        # ...
    ],
    # ...
)

ヒント

テストログの読み取り

--test_output=errors を使用して失敗したテストのログを出力するか、--test_output=all を使用してすべてのテスト出力を出力します。もし テストログを確認するには、 $PROJECT_ROOT/bazel-testlogs/path/to/InstrumentationTestTargetName

たとえば、BasicSample カノニカル プロジェクトのテストログが bazel-testlogs/ui/espresso/BasicSample/BasicSampleInstrumentationTest にある場合は、次のコマンドを実行します。

tree bazel-testlogs/ui/espresso/BasicSample/BasicSampleInstrumentationTest

これにより、次の出力が得られます。


$ tree bazel-testlogs/ui/espresso/BasicSample/BasicSampleInstrumentationTest
.
├── adb.409923.log
├── broker_logs
│   ├── aapt_binary.10.ok.txt
│   ├── aapt_binary.11.ok.txt
│   ├── adb.12.ok.txt
│   ├── adb.13.ok.txt
│   ├── adb.14.ok.txt
│   ├── adb.15.fail.txt
│   ├── adb.16.ok.txt
│   ├── adb.17.fail.txt
│   ├── adb.18.ok.txt
│   ├── adb.19.fail.txt
│   ├── adb.20.ok.txt
│   ├── adb.21.ok.txt
│   ├── adb.22.ok.txt
│   ├── adb.23.ok.txt
│   ├── adb.24.fail.txt
│   ├── adb.25.ok.txt
│   ├── adb.26.fail.txt
│   ├── adb.27.ok.txt
│   ├── adb.28.fail.txt
│   ├── adb.29.ok.txt
│   ├── adb.2.ok.txt
│   ├── adb.30.ok.txt
│   ├── adb.3.ok.txt
│   ├── adb.4.ok.txt
│   ├── adb.5.ok.txt
│   ├── adb.6.ok.txt
│   ├── adb.7.ok.txt
│   ├── adb.8.ok.txt
│   ├── adb.9.ok.txt
│   ├── android_23_x86.1.ok.txt
│   └── exec-1
│       ├── adb-2.txt
│       ├── emulator-2.txt
│       └── mksdcard-1.txt
├── device_logcat
│   └── logcat1635880625641751077.txt
├── emulator_itCqtc.log
├── outputs.zip
├── pipe.log.txt
├── telnet_pipe.log.txt
└── tmpuRh4cy
    ├── watchdog.err
    └── watchdog.out

4 directories, 41 files

エミュレータ ログの読み取り

android_device ターゲットのエミュレータ ログは、emulator_xxxxx.log という名前で /tmp/ ディレクトリに保存されます。ここで、xxxxx はランダムに生成された文字列です。

次のコマンドを使用して、最新のエミュレータ ログを確認します。

ls -1t /tmp/emulator_*.log | head -n 1

複数の API レベルのテスト

複数の API レベルに対してテストする場合は、リスト コンプリヘンションを使用して、各 API レベルのテスト対象を作成できます。例:

API_LEVELS = [
    "19",
    "20",
    "21",
    "22",
]

[android_instrumentation_test(
    name = "my_test_%s" % API_LEVEL,
    test_app = ":my_test_app",
    target_device = "@android_test_support//tools/android/emulated_devices/generic_phone:android_%s_x86_qemu2" % API_LEVEL,
) for API_LEVEL in API_LEVELS]

既知の問題

  • フォークした adb サーバー プロセスが、 テスト
  • APK のビルドはすべてのプラットフォーム(Linux、macOS、Windows)で機能しますが、テストは Linux でのみ機能します。
  • --config=local_adb を使用している場合でも、android_instrumentation_test.target_device を指定する必要があります。
  • ローカル デバイスまたはエミュレータを使用している場合、Bazel による APK のアンインストールが テストします。次のコマンドを実行してパッケージをクリーンアップします。
adb shell pm list
packages com.example.android.testing | cut -d ':' -f 2 | tr -d '\r' | xargs
-L1 -t adb uninstall