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