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

วันที่ รายงานปัญหา ดูแหล่งที่มา ตอนกลางคืน · 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 Bazel ต้องใช้การทดสอบแบบไม่มีส่วนหัว (เช่น ในเซิร์ฟเวอร์ CI) X VirtualFramebuffer

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

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

วิธีหนึ่งในการจัดระเบียบทรัพยากร 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

ตัวอย่างเช่น บันทึกการทดสอบสำหรับโปรเจ็กต์ Canonical 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