หากเพิ่งเริ่มใช้ Bazel ให้เริ่มด้วยบทแนะนำการสร้าง Android ด้วย Bazel
รูปที่ 1 การทดสอบการใช้เครื่องมือ Android แบบขนาน
android_instrumentation_test
ช่วยให้นักพัฒนาแอปทดสอบแอปในโปรแกรมจำลองและอุปกรณ์ Android ได้
โดยจะใช้ API เฟรมเวิร์ก Android จริงและไลบรารีทดสอบ Android
Bazel จะสร้างและเปิดใช้งานโปรแกรมจําลอง Android ในแซนด์บ็อกซ์เพื่อให้การทดสอบทํางานจากสถานะที่สะอาดอยู่เสมอ เพื่อรักษาความสมบูรณ์และความสามารถในการทําซ้ำ การทดสอบแต่ละรายการจะได้รับอินสแตนซ์โปรแกรมจำลองแบบแยกต่างหาก ซึ่งช่วยให้การทดสอบทํางานแบบคู่ขนานได้โดยไม่ต้องส่งผ่านสถานะระหว่างกัน
ดูข้อมูลเพิ่มเติมเกี่ยวกับการทดสอบเครื่องมือวัดผลของ Android ได้ในเอกสารประกอบสำหรับนักพัฒนาแอป Android
โปรดส่งปัญหาในเครื่องมือติดตามปัญหา GitHub
วิธีการทำงาน
เมื่อคุณเรียกใช้ bazel test
ในเป้าหมาย android_instrumentation_test
เป็นครั้งแรก Bazel จะทําตามขั้นตอนต่อไปนี้
- สร้าง APK การทดสอบ, APK ภายใต้การทดสอบ และทรัพยากรที่เกี่ยวข้อง
- สร้าง บูต และแคชสถานะโปรแกรมจำลองที่สะอาด
- เริ่มโปรแกรมจำลอง
- ติดตั้ง APK
- เรียกใช้การทดสอบโดยใช้ Android Test Orchestrator
- ปิดโปรแกรมจำลอง
- รายงานผลลัพธ์
ในการทดสอบครั้งต่อๆ ไป Bazel จะบูตโปรแกรมจำลองจากสถานะที่ล้างแคชแล้วซึ่งสร้างขึ้นในขั้นตอนที่ 2 จึงไม่มีสถานะที่เหลืออยู่จากการเรียกใช้ครั้งก่อน การแคชสถานะโปรแกรมจำลองยังช่วยเพิ่มความเร็วในการทดสอบด้วย
ข้อกำหนดเบื้องต้น
ตรวจสอบว่าสภาพแวดล้อมของคุณมีคุณสมบัติตรงตามข้อกําหนดเบื้องต้นต่อไปนี้
Linux ทดสอบใน Ubuntu 16.04 และ 18.04
Bazel 0.12.0 ขึ้นไป ยืนยันเวอร์ชันโดยเรียกใช้
bazel info release
bazel info release
ซึ่งจะให้ผลลัพธ์ที่คล้ายกับตัวอย่างต่อไปนี้
release 4.1.0
- KVM Bazel กำหนดให้โปรแกรมจำลองต้องมีการเร่งด้วยฮาร์ดแวร์ด้วย KVM ใน Linux คุณสามารถทำตามวิธีการติดตั้งเหล่านี้สำหรับ Ubuntu
หากต้องการตรวจสอบว่า 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
มีดังนี้
รูปที่ 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 และ UIAutomatorandroid_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 เท่านั้น ใช้เป้าหมาย 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
ใช้การกำหนดค่าเพียงรายการเดียว ไม่เช่นนั้นการทดสอบจะล้มเหลว
การทดสอบแบบ Headless
Xvfb
ช่วยให้ทดสอบด้วยโปรแกรมจำลองได้โดยไม่ต้องมีอินเทอร์เฟซแบบกราฟิก หรือที่เรียกว่าการทดสอบแบบ Headless หากต้องการปิดใช้อินเทอร์เฟซแบบกราฟิกเมื่อทำการทดสอบ ให้ส่งอาร์กิวเมนต์ทดสอบ --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
โปรเจ็กต์ตัวอย่าง
หากต้องการดูตัวอย่างโปรเจ็กต์ตามรูปแบบมาตรฐาน โปรดดูตัวอย่างการทดสอบ 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",
],
)
จากนั้นเพิ่มการพึ่งพาที่จำเป็นใน 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 หลายระดับ คุณสามารถใช้ลิสต์คอมเพรสชันเพื่อสร้างเป้าหมายการทดสอบสําหรับ 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