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

รายงานปัญหา ดูแหล่งที่มา /3} /4} {3/4} {3/4} {3/4} {3/4} /4.

หากคุณเพิ่งเริ่มใช้ 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 Bazel ต้องใช้ X Virtual Framebuffer เพื่อทำการทดสอบแบบไม่มีส่วนหัว (เช่น ในเซิร์ฟเวอร์ CI)

หากต้องการติดตั้ง ให้เรียกใช้คำสั่งต่อไปนี้

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 ดังนี้

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 ดูรุ่นล่าสุดได้ที่นี่

เปิดใช้ทรัพยากร Dependency เหล่านี้โดยเพิ่มบรรทัดต่อไปนี้ลงในไฟล์ 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

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

การทดสอบแบบไม่มีส่วนหัว

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

วิธีหนึ่งในการจัดระเบียบทรัพยากร Dependency เหล่านี้คือการสร้างไลบรารีที่ใช้ร่วมกันของ //: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 โปรเจ็กต์ 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 หลายระดับ ให้ใช้ความเข้าใจระดับรายการเพื่อสร้างเป้าหมายการทดสอบสำหรับ 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