اختبارات أدوات Android

إذا كنت مستخدمًا جديدًا لتطبيق Bazel، يمكنك بدء البرنامج التعليمي الخاص بـ Building Android مع Bazel.

إجراء اختبارات قياس أداء Android بالتوازي

الشكل 1. إجراء اختبارات قياس أداء Android المتوازية

android_instrumentation_test تسمح لمطوّري البرامج باختبار تطبيقاتهم على أجهزة Android ومحاكياتها. وهي تستخدم واجهات برمجة تطبيقات إطار عمل Android الفعلية ومكتبة اختبار Android.

وحرصًا منا على قابلية الإضافة وقابلية الظهور، تنشئ Bazel مُحاكيات لنظام التشغيل Android وتطلقها في وضع الحماية، ما يضمن تشغيل الاختبارات دائمًا من حالة نظيفة. يحصل كل اختبار على مثيل محاكي معزول، ما يسمح بتشغيل الاختبارات بالتوازي بدون تمرير الحالات بينها.

لمزيد من المعلومات حول اختبارات أدوات Android، يُرجى مراجعة وثائق مطوّري برامج Android.

يُرجى الإبلاغ عن المشاكل في أداة تتبُّع المشاكل في GitHub.

آلية العمل

عند تنفيذ bazel test على الهدف android_instrumentation_test للمرة الأولى، ينفِّذ Bazel الخطوات التالية:

  1. إنشاء ملف APK تجريبي وحزمة APK قيد الاختبار واعتمادياتها المباشرة
  2. إنشاء حالات محاكية والتشغيل والتخزين المؤقت في ذاكرة التخزين المؤقت
  3. بدء المحاكي
  4. تثبيت ملفات APK
  5. تنفيذ الاختبارات باستخدام مُنظِّم اختبار Android
  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

لتثبيته، شغِّل:

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:

الرسم البياني للتبعية المستهدفة على اختبار قياس حالة تطبيق Android

الشكل 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