إذا كنت مستخدمًا جديدًا لتطبيق Bazel، يمكنك بدء البرنامج التعليمي الخاص بـ Building Android مع Bazel.
الشكل 1. إجراء اختبارات قياس أداء Android المتوازية
android_instrumentation_test
تسمح لمطوّري البرامج باختبار تطبيقاتهم على أجهزة Android ومحاكياتها.
وهي تستخدم واجهات برمجة تطبيقات إطار عمل Android الفعلية ومكتبة اختبار Android.
وحرصًا منا على قابلية الإضافة وقابلية الظهور، تنشئ Bazel مُحاكيات لنظام التشغيل Android وتطلقها في وضع الحماية، ما يضمن تشغيل الاختبارات دائمًا من حالة نظيفة. يحصل كل اختبار على مثيل محاكي معزول، ما يسمح بتشغيل الاختبارات بالتوازي بدون تمرير الحالات بينها.
لمزيد من المعلومات حول اختبارات أدوات Android، يُرجى مراجعة وثائق مطوّري برامج Android.
يُرجى الإبلاغ عن المشاكل في أداة تتبُّع المشاكل في GitHub.
آلية العمل
عند تنفيذ bazel test
على الهدف android_instrumentation_test
للمرة الأولى، ينفِّذ Bazel الخطوات التالية:
- إنشاء ملف APK تجريبي وحزمة APK قيد الاختبار واعتمادياتها المباشرة
- إنشاء حالات محاكية والتشغيل والتخزين المؤقت في ذاكرة التخزين المؤقت
- بدء المحاكي
- تثبيت ملفات APK
- تنفيذ الاختبارات باستخدام مُنظِّم اختبار Android
- إيقاف المحاكي
- تقديم تقرير بالنتائج
في عمليات الاختبار اللاحقة، يُشغِّل 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 الذاكرة المؤقتة الافتراضية للإطارات.
لتثبيته، شغِّل:
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
البدء
في ما يلي رسم بياني لاعتمادية نموذجية على android_instrumentation_test
:
الشكل 2. الرسم البياني للتبعية المستهدفة 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>
تبعيات WORKSPACE
لاستخدام هذه القاعدة، يجب أن يعتمد مشروعك على المستودعات الخارجية التالية:
@androidsdk
: حزمة تطوير البرامج (SDK) لنظام التشغيل Android يمكنك تنزيل هذا التطبيق من خلال 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()
تبعيات Maven
لإدارة الاعتماديات على عناصر Maven من المستودعات، مثل Google Maven أو Maven Central، عليك استخدام برنامج تعيين Maven، مثل rules_jvm_external
.
توضح بقية هذه الصفحة كيفية استخدام rules_jvm_external
لحل التبعيات وجلبها من مستودعات 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
للمستوى المحدّد لواجهة برمجة التطبيقات. لتنزيل صورة النظام، استخدِم حزمة تطوير البرامج (SDK) لنظام التشغيل Android.
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
لتفعيل وضع الاختبار المحلي.
إذا كان هناك أكثر من جهاز متصل واحد، فمرِّر العلامة --test_arg=--device_serial_number=$device_id
حيث يكون $device_id
هو معرف الجهاز/المحاكي المدرج في adb devices
.
نماذج مشاريع
إذا كنت تبحث عن عيّنات مشاريع أساسية، يُرجى الاطّلاع على عيّنات اختبار Android للمشاريع التي تستخدم Espresso وUIAutomator.
إعداد الإسبريسو
إذا كتبت اختبارات واجهة المستخدم باستخدام Espresso
(androidx.test.espresso
)، يمكنك استخدام المقتطفات التالية لإعداد
مساحة عمل Basel مع قائمة عناصر 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",
],
)
أخيرًا، في هدف الاختبار 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_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]
المشاكل المعروفة
- لا يتم إنهاء عمليات خادم Adbs المتشعّبة بعد إجراء الاختبارات
- على الرغم من أن تصميم حزمة 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