Bazel を初めて使用する場合は、Terraform を使用した Android のビルド Bazel のチュートリアルをご覧ください。
図 1. Android インストルメンテーション テストの並列実行。
android_instrumentation_test
を使用すると、デベロッパーは Android Emulator とデバイスでアプリをテストできます。実際の Android フレームワーク API と Android テスト ライブラリを利用します。
密閉性と再現性を確保するため、Bazel は Android を作成して起動します。 サンドボックス内でエミュレータを使用して、テストを常にクリーンな状態から実行できます。各テストは分離されたエミュレータ インスタンスを取得するため、テスト間で状態を渡すことなくテストを並列実行できます。
Android インストルメンテーション テストの詳細については、Android デベロッパー向けドキュメントをご覧ください。
問題については、GitHub 公開バグトラッカーで報告してください。
仕組み
android_instrumentation_test
ターゲットで bazel test
を初めて実行すると、Bazel は次の手順を実行します。
- テスト APK、テスト対象の APK、およびそれらの推移的依存関係をビルドします
- クリーンなエミュレータの状態を作成、起動、キャッシュに保存する
- エミュレータを起動します。
- APK をインストールします。
- Android Test Orchestrator を使用してテストを実行します。
- エミュレータをシャットダウンする
- 結果を報告する
以降のテスト実行では、Bazel はステップ 2 で作成されたクリーンなキャッシュに保存された状態からエミュレータを起動するため、以前の実行からの状態が残りません。キャッシュ エミュレータの状態によってテスト実行も高速化されます。
前提条件
環境が次の前提条件を満たしていることを確認します。
Linux。Ubuntu 16.04、18.04 でテスト済みです。
Bazel 0.12.0 以降。
bazel info release
を実行してバージョンを確認します。
bazel info release
これにより、次のような出力が生成されます。
release 4.1.0
- KVM。Bazel では、エミュレータに Linux で KVM を使用したハードウェア アクセラレーションが必要です。Ubuntu の場合は、インストール手順に沿って操作します。
KVM が正しく構成されていることを確認するには、次のコマンドを実行します。
apt-get install cpu-checker && kvm-ok
次のメッセージが表示されたら、構成が正しいことを示します。
INFO: /dev/kvm exists
KVM acceleration can be used
- Xvfb。ヘッドレス テストを(CI サーバーなどで)実行するには、Bazel に以下が必要です。 X 仮想フレームバッファ。
インストールするには、次のコマンドを実行します。
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
の一般的なターゲット依存関係グラフを次に示します。
図 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 Maven や Maven 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_id
は adb 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