การทดสอบการใช้เครื่องมือ Android

รายงานปัญหา ดูแหล่งที่มา Nightly · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

หากเพิ่งเริ่มใช้ Bazel ให้เริ่มจากบทแนะนำการสร้าง Android ด้วย Bazel

การเรียกใช้การทดสอบการวัดคุมของ Android แบบขนาน

รูปที่ 1 การเรียกใช้การทดสอบการวัดคุม Android แบบคู่ขนาน

android_instrumentation_test ช่วยให้นักพัฒนาแอปทดสอบแอปของตนในโปรแกรมจำลองและอุปกรณ์ Android ได้ โดยใช้ API ของเฟรมเวิร์ก Android จริงและ Android Test Library

เพื่อความสมบูรณ์และความสามารถในการทำซ้ำ Bazel จะสร้างและเปิดใช้โปรแกรมจำลอง Android ในแซนด์บ็อกซ์เพื่อให้มั่นใจว่าการทดสอบจะทำงานจากสถานะที่สะอาดเสมอ การทดสอบแต่ละรายการจะมีอินสแตนซ์โปรแกรมจำลองที่แยกกัน ทำให้การทดสอบสามารถทำงานแบบคู่ขนานได้โดยไม่ต้องส่งต่อสถานะระหว่างกัน

ดูข้อมูลเพิ่มเติมเกี่ยวกับการทดสอบเครื่องมือ Android ได้ที่เอกสารประกอบสำหรับนักพัฒนาซอฟต์แวร์ Android

โปรดแจ้งปัญหาในเครื่องมือติดตามปัญหา GitHub

วิธีการทำงาน

เมื่อเรียกใช้ bazel test ในเป้าหมาย android_instrumentation_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

เริ่มต้นใช้งาน

นี่คือกราฟทรัพยากร Dependency เป้าหมายทั่วไปของ android_instrumentation_test

กราฟทรัพยากร Dependency เป้าหมายในการทดสอบเครื่องมือ Android

รูปที่ 2 กราฟทรัพยากร Dependency เป้าหมายของ 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 เป้าหมายนี้มีโค้ดทดสอบและ การอ้างอิง เช่น Espresso และ UIAutomator android_binary เป้าหมายที่เลือกต้องระบุแอตทริบิวต์ instruments ที่ชี้ไปยังandroid_binary อื่น ซึ่งเป็นแอปที่อยู่ระหว่างการทดสอบ

  • target_device: เป้าหมาย android_device เป้าหมายนี้อธิบาย ข้อกำหนดของโปรแกรมจำลอง Android ที่ 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>

ทรัพยากร Dependency ของ 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()

ทรัพยากร Dependency ของ Maven

หากต้องการจัดการทรัพยากร Dependency ในอาร์ติแฟกต์ Maven จากที่เก็บ เช่น Google Maven หรือ Maven 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

คุณต้องมี system_image สำหรับ API ระดับที่เลือกจึงจะเปิดใช้ android_device ได้ หากต้องการดาวน์โหลดอิมเมจระบบ ให้ใช้ tools/bin/sdkmanager ของ Android SDK เช่น หากต้องการดาวน์โหลดอิมเมจระบบสำหรับ 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 เท่านั้น ใช้เป้าหมาย 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 เพื่อเปิดใช้โหมดทดสอบในเครื่อง หากมีอุปกรณ์ที่เชื่อมต่อมากกว่า 1 เครื่อง ให้ส่งแฟล็ก --test_arg=--device_serial_number=$device_id โดยที่ $device_id คือรหัสของ อุปกรณ์/โปรแกรมจำลองที่แสดงใน adb devices

โปรเจ็กต์ตัวอย่าง

หากกำลังมองหาตัวอย่างโปรเจ็กต์ Canonical โปรดดูตัวอย่างการทดสอบ Android สำหรับโปรเจ็กต์ที่ใช้ Espresso และ UIAutomator

การตั้งค่าเอสเปรสโซ

หากเขียนการทดสอบ UI ด้วย Espresso (androidx.test.espresso) คุณจะใช้ข้อมูลโค้ดต่อไปนี้เพื่อตั้งค่าพื้นที่ทำงาน Bazel ด้วยรายการอาร์ติแฟกต์ Espresso ที่ใช้กันโดยทั่วไปและทรัพยากร Dependency ของอาร์ติแฟกต์เหล่านั้นได้

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

วิธีหนึ่งในการจัดระเบียบการอ้างอิงเหล่านี้คือการสร้าง//:test_depsไลบรารีที่แชร์ 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",
    ],
)

จากนั้นเพิ่มการอ้างอิงที่จำเป็นใน 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",
    ],
)

สุดท้าย ให้เพิ่ม //:test_deps dependency ในandroid_binaryเป้าหมายการทดสอบ

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 Canonical อยู่ใน 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 หลายระดับ คุณสามารถใช้ List comprehension เพื่อสร้างเป้าหมายการทดสอบสำหรับ 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