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

รายงานปัญหา ดูแหล่งที่มา รุ่น Nightly · 7.4 7.3 · 7.2 · 7.1 · 7.0 · 6.5

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

การเรียกใช้การทดสอบการใช้เครื่องมือของ Android พร้อมกัน

รูปที่ 1 การทดสอบการใช้เครื่องมือ Android แบบขนาน

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

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
  • Xvfb หากต้องการเรียกใช้การทดสอบแบบไม่มีส่วนแสดงผล (เช่น ในเซิร์ฟเวอร์ CI) Bazel ต้องใช้ X virtual 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

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

กราฟทรัพยากร 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 เพื่อแก้ปัญหาและดึงข้อมูลทรัพยากร Dependency จากที่เก็บของ 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 คุณต้องใช้ system_image สำหรับระดับ API ที่เลือก หากต้องการดาวน์โหลดอิมเมจระบบ ให้ใช้ 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 เท่านั้น โปรดใช้เป้าหมาย android_device QEMU2 รายการแทน 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

ใช้การกําหนดค่าเพียงรายการเดียว มิฉะนั้นการทดสอบจะไม่สําเร็จ

การทดสอบแบบ Headless

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 ยังรองรับการทดสอบโดยตรงในโปรแกรมจำลองที่เปิดในเครื่องหรืออุปกรณ์ที่เชื่อมต่อ ส่ง Flag --test_strategy=exclusive และ --test_arg=--device_broker_type=LOCAL_ADB_SERVER เพื่อเปิดใช้โหมดการทดสอบในเครื่อง หากมีอุปกรณ์ที่เชื่อมต่อมากกว่า 1 เครื่อง ให้ส่ง Flag --test_arg=--device_serial_number=$device_id โดย $device_id คือรหัสของอุปกรณ์/โปรแกรมจำลองที่ระบุไว้ใน adb devices

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

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

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

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

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

จากนั้นเพิ่มทรัพยากร Dependency ที่จำเป็นใน 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 shell pm list
packages com.example.android.testing | cut -d ':' -f 2 | tr -d '\r' | xargs
-L1 -t adb uninstall