Android 檢測設備測試

回報問題 查看來源

如果您是第一次使用 Bazel,請先參閱「使用 Bazel 建構 Android」教學課程。

同時執行 Android 檢測設備測試

圖 1 執行平行 Android 檢測設備測試。

android_instrumentation_test 可讓開發人員在 Android 模擬器和裝置上測試應用程式。它採用實際的 Android 架構 API 和 Android 測試程式庫。

為求保存性和可重現性,Bazel 會在沙箱中建立並啟動 Android 模擬器,確保系統一律會從乾淨的狀態執行測試。每項測試都會取得獨立的模擬器執行個體,讓測試可以平行執行,而不傳遞狀態。

如要進一步瞭解 Android 檢測設備測試,請參閱 Android 開發人員說明文件

請透過 GitHub Issue Tracker 回報問題。

運作方式

當您首次在 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
  • Xvfb:如要執行無頭測試 (例如在持續整合伺服器上),Bazel 需要 X 虛擬 framebuffer

如要安裝這個套件,請執行以下指令:

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 檔案,如下所示:

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_appandroid_binary 的目標。這個目標包含測試程式碼和依附元件,例如 Espresso 和 UIAutomator。必須選取 android_binary 目標才能指定指向另一個 android_binaryinstruments 屬性,也就是測試中的應用程式。

  • target_deviceandroid_device 的目標。這個目標會說明 Bazel 用來建立、啟動及執行測試的 Android 模擬器規格。詳情請參閱「選擇 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 依附元件

為管理 Maven 構件中的依附元件,例如 Google MavenMaven Central,建議您使用 Maven 解析器,例如 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_test_support 中支援的 android_device 目標完整清單,請執行下列指令:

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

Bazel 目前僅支援以 x86 為基礎的模擬器。為提高效能,請使用 QEMU2 android_device 目標,而非 QEMU 目標。

執行測試

如要執行測試,請將以下幾行新增至專案的 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

只使用一項設定,否則測試會失敗。

無頭測試

使用 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 (androidx.test.espresso) 編寫 UI 測試,可以使用下列程式碼片段,根據常用的 Espresso 構件及其依附元件清單設定 Bazel 工作區:

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

整理這些依附元件的方法之一,是在 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 記錄會儲存在 /tmp/ 目錄中,名稱為 emulator_xxxxx.log,其中 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