Kiểm thử khả năng đo lường trên Android

Báo cáo sự cố Xem nguồn

Nếu bạn mới sử dụng Bazel, hãy bắt đầu với hướng dẫn Xây dựng Android bằng Bazel.

Chạy song song các bài kiểm thử đo lường trên Android

Hình 1. Chạy kiểm thử đo lường song song cho Android.

android_instrumentation_test cho phép nhà phát triển kiểm thử ứng dụng của họ trên trình mô phỏng và thiết bị Android. Công cụ này sử dụng API khung Android thực và Thư viện kiểm thử Android.

Đối với tính mạnh và khả năng tái tạo, Bazel tạo và khởi chạy các trình mô phỏng Android trong một hộp cát, đảm bảo rằng các hoạt động kiểm thử luôn chạy ở trạng thái sạch. Mỗi lượt kiểm thử sẽ nhận được một thực thể trình mô phỏng riêng biệt, cho phép các lượt kiểm thử chạy song song mà không cần chuyển trạng thái giữa các lượt kiểm thử đó.

Để biết thêm thông tin về kiểm thử đo lường Android, vui lòng xem Tài liệu dành cho nhà phát triển Android.

Vui lòng báo cáo vấn đề trong công cụ theo dõi lỗi GitHub.

Cách thức hoạt động

Trong lần đầu tiên bạn chạy bazel test trên mục tiêu android_instrumentation_test, Bazel sẽ thực hiện các bước sau:

  1. Xây dựng tệp APK kiểm thử, tệp APK đang kiểm thử và các phần phụ thuộc bắc cầu tương ứng
  2. Tạo, khởi động và lưu vào bộ nhớ đệm xoá trạng thái trình mô phỏng
  3. Khởi động trình mô phỏng
  4. Cài đặt APK
  5. Chạy kiểm thử bằng Android Test Orchestrator
  6. Tắt trình mô phỏng
  7. Báo cáo kết quả

Trong các lần chạy kiểm thử tiếp theo, Bazel khởi động trình mô phỏng từ trạng thái sạch, đã lưu vào bộ nhớ đệm được tạo ở bước 2, do đó, không còn trạng thái nào còn lại trong các lần chạy trước. Việc lưu trạng thái trình mô phỏng vào bộ nhớ đệm cũng tăng tốc độ chạy kiểm thử.

Điều kiện tiên quyết

Đảm bảo môi trường của bạn đáp ứng các điều kiện tiên quyết sau:

  • Linux. Đã thử nghiệm trên Ubuntu 16.04 và 18.04.

  • Bazel 0.12.0 trở lên. Xác minh phiên bản bằng cách chạy bazel info release.

bazel info release

Kết quả sẽ tương tự như sau:

release 4.1.0

Để xác minh rằng KVM có cấu hình chính xác, hãy chạy:

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

Nếu chế độ này in thông báo sau đây, tức là bạn đã có cấu hình chính xác:

INFO: /dev/kvm exists
KVM acceleration can be used
  • Xvfb. Để chạy các kiểm thử không có giao diện người dùng (ví dụ: trên máy chủ CI), Bazel yêu cầu bộ đệm khung ảo X.

Để cài đặt ứng dụng này, hãy chạy:

apt-get install xvfb

Xác minh rằng Xvfb được cài đặt đúng cách và được cài đặt tại /usr/bin/Xvfb bằng cách chạy:

which Xvfb

Kết quả sẽ như sau:

/usr/bin/Xvfb
  • Thư viện 32 bit. Một số tệp nhị phân mà cơ sở hạ tầng kiểm thử sử dụng là 32 bit. Vì vậy, trên máy 64 bit, hãy đảm bảo rằng tệp nhị phân 32 bit có thể chạy được. Đối với Ubuntu, hãy cài đặt các thư viện 32 bit sau:
sudo apt-get install libc6:i386 libncurses5:i386 libstdc++6:i386 lib32z1 libbz2-1.0:i386

Bắt đầu

Dưới đây là biểu đồ phần phụ thuộc mục tiêu điển hình của android_instrumentation_test:

Biểu đồ phần phụ thuộc mục tiêu trong bài kiểm thử đo lường Android

Hình 2. Biểu đồ phần phụ thuộc mục tiêu của android_instrumentation_test.

BUILD tệp

Biểu đồ chuyển thành tệp BUILD như sau:

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",
    ]
    # ...
)

Các thuộc tính chính của quy tắc android_instrumentation_test là:

  • test_app: Mục tiêu android_binary. Mục tiêu này chứa mã kiểm thử và các phần phụ thuộc như Espresso và UIAutomator. Bạn cần phải chỉ định mục tiêu android_binary đã chọn để chỉ định một thuộc tính instruments trỏ đến một android_binary khác, đây là ứng dụng đang được kiểm thử.

  • target_device: Mục tiêu android_device. Mục tiêu này mô tả các thông số kỹ thuật của trình mô phỏng Android mà Bazel sử dụng để tạo, khởi chạy và chạy kiểm thử. Hãy xem phần cách chọn thiết bị Android để biết thêm thông tin.

AndroidManifest.xml của ứng dụng kiểm thử phải bao gồm một thẻ <instrumentation>. Thẻ này phải chỉ định các thuộc tính cho gói ứng dụng mục tiêutên lớp đủ điều kiện của trình chạy kiểm thử đo lường, androidx.test.runner.AndroidJUnitRunner.

Dưới đây là AndroidTestManifest.xml mẫu cho ứng dụng kiểm thử:

<?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>

Phần phụ thuộc WORKSPACE

Để sử dụng quy tắc này, dự án của bạn cần phụ thuộc vào các kho lưu trữ bên ngoài sau:

  • @androidsdk: SDK Android. Hãy tải dữ liệu này xuống qua Android Studio.

  • @android_test_support: Lưu trữ trình chạy kiểm thử, trình chạy trình mô phỏng và mục tiêu android_device. Bạn có thể tìm thấy bản phát hành mới nhất tại đây.

Bật các phần phụ thuộc này bằng cách thêm các dòng sau vào tệp 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()

Phần phụ thuộc Maven

Để quản lý các phần phụ thuộc trên cấu phần phần mềm Maven từ các kho lưu trữ, chẳng hạn như Google Maven hoặc Maven Central, bạn nên sử dụng trình phân giải Maven, chẳng hạn như rules_jvm_external.

Phần còn lại của trang này cho biết cách sử dụng rules_jvm_external để phân giải và tìm nạp các phần phụ thuộc qua kho lưu trữ Maven.

Chọn mục tiêu android_device

android_instrumentation_test.target_device chỉ định thiết bị Android mà bạn muốn chạy kiểm thử. Các mục tiêu android_device này được xác định trong @android_test_support.

Ví dụ: bạn có thể truy vấn các nguồn cho một mục tiêu cụ thể bằng cách chạy:

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

Kết quả nào sẽ tạo ra kết quả trông giống như:

# .../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",
)

Tên mục tiêu của thiết bị sử dụng mẫu này:

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

Để chạy android_device, bạn phải có system_image cho cấp độ API đã chọn. Để tải hình ảnh hệ thống xuống, hãy sử dụng tools/bin/sdkmanager của SDK Android. Ví dụ: để tải hình ảnh hệ thống xuống cho generic_phone:android_23_x86, hãy chạy $sdk/tools/bin/sdkmanager "system-images;android-23;default;x86".

Để xem danh sách đầy đủ các mục tiêu android_device được hỗ trợ trong @android_test_support, hãy chạy lệnh sau:

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

Bazel hiện chỉ hỗ trợ các trình mô phỏng dựa trên x86. Để đạt được hiệu suất tốt hơn, hãy sử dụng mục tiêu QEMU2 android_device thay vì mục tiêu QEMU.

Chạy kiểm thử

Để chạy kiểm thử, hãy thêm các dòng này vào tệp project root:/.bazelrc của dự án.

# 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

Sau đó, hãy dùng một trong các cấu hình sau để chạy kiểm thử:

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

Chỉ sử dụng một cấu hình, nếu không thì bài kiểm thử sẽ không thành công.

Kiểm thử không có giao diện người dùng

Với Xvfb, bạn có thể kiểm thử bằng các trình mô phỏng mà không cần giao diện đồ hoạ, còn được gọi là kiểm thử không có giao diện người dùng. Để tắt giao diện đồ hoạ khi chạy kiểm thử, hãy chuyển đối số kiểm thử --enable_display=false đến Bazel:

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

Kiểm thử GUI

Nếu đặt biến môi trường $DISPLAY, bạn có thể bật giao diện đồ hoạ của trình mô phỏng trong khi kiểm thử đang chạy. Để thực hiện việc này, hãy truyền các đối số kiểm thử này cho Bazel:

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

Kiểm thử bằng trình mô phỏng hoặc thiết bị cục bộ

Bazel cũng hỗ trợ kiểm thử trực tiếp trên trình mô phỏng hoặc thiết bị được kết nối đã khởi chạy cục bộ. Truyền các cờ --test_strategy=exclusive--test_arg=--device_broker_type=LOCAL_ADB_SERVER để bật chế độ kiểm thử cục bộ. Nếu có nhiều thiết bị được kết nối, hãy truyền cờ --test_arg=--device_serial_number=$device_id, trong đó $device_id là mã nhận dạng của thiết bị/trình mô phỏng được liệt kê trong adb devices.

Dự án mẫu

Nếu bạn đang tìm mẫu dự án chính tắc, hãy xem mẫu kiểm thử Android cho các dự án sử dụng Espresso và UIAutomator.

Thiết lập Espresso

Nếu viết kiểm thử giao diện người dùng bằng Espresso (androidx.test.espresso), bạn có thể sử dụng các đoạn mã sau để thiết lập không gian làm việc Bazel bằng danh sách các cấu phần phần mềm Espresso thường dùng và các phần phụ thuộc của chúng:

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

Một cách để sắp xếp các phần phụ thuộc này là tạo thư viện chia sẻ //:test_deps trong tệp project root/BUILD.bazel:

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",
    ],
)

Sau đó, hãy thêm các phần phụ thuộc bắt buộc trong 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",
    ],
)

Cuối cùng, trong mục tiêu android_binary kiểm thử, hãy thêm phần phụ thuộc //:test_deps:

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

Mẹo

Đọc nhật ký kiểm thử

Sử dụng --test_output=errors để in nhật ký cho các lượt kiểm thử không thành công hoặc --test_output=all để in tất cả kết quả kiểm thử. Nếu bạn đang tìm một nhật ký kiểm thử riêng lẻ, hãy truy cập $PROJECT_ROOT/bazel-testlogs/path/to/InstrumentationTestTargetName.

Ví dụ: nhật ký kiểm tra của dự án chính tắc BasicSample nằm trong bazel-testlogs/ui/espresso/BasicSample/BasicSampleInstrumentationTest, hãy chạy:

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

Kết quả như sau:


$ 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

Đọc nhật ký của trình mô phỏng

Nhật ký trình mô phỏng cho các mục tiêu android_device được lưu trữ trong thư mục /tmp/ có tên emulator_xxxxx.log, trong đó xxxxx là một trình tự ký tự được tạo ngẫu nhiên.

Dùng lệnh này để tìm nhật ký mới nhất của trình mô phỏng:

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

Kiểm thử nhiều cấp độ API

Nếu muốn kiểm thử nhiều cấp độ API, bạn có thể sử dụng khả năng hiểu danh sách để tạo mục tiêu kiểm thử cho từng cấp độ API. Ví dụ:

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]

Vấn đề đã biết

  • Các quy trình của máy chủ adb phân nhánh không bị chấm dứt sau khi kiểm thử
  • Mặc dù tính năng tạo tệp APK hoạt động trên tất cả các nền tảng (Linux, macOS, Windows), nhưng việc kiểm thử chỉ hoạt động trên Linux.
  • Ngay cả với --config=local_adb, người dùng vẫn cần chỉ định android_instrumentation_test.target_device.
  • Nếu bạn sử dụng trình mô phỏng hoặc thiết bị cục bộ, Bazel sẽ không gỡ cài đặt các tệp APK sau quá trình kiểm thử. Bạn có thể dọn dẹp các gói bằng cách chạy lệnh sau:
adb shell pm list
packages com.example.android.testing | cut -d ':' -f 2 | tr -d '\r' | xargs
-L1 -t adb uninstall