בדיקות אינסטרומנטציה ב-Android

אם אתם חדשים ב-Bazel, התחילו עם המדריך Building Android with 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
  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 מהמאגרים, כמו 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 ברמת ה-API שנבחרה. כדי להוריד את תמונת המערכת, יש להשתמש ב-Android SDK&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

יש להשתמש בתצורה אחת בלבד או שהבדיקות ייכשלו.

בדיקה ללא GUI

באמצעות Xvfb, ניתן לבצע בדיקות באמצעות אמולטורים ללא הממשק הגרפי, המכונה גם בדיקה ללא GUI. כדי להשבית את הממשק הגרפי כשמריצים בדיקות, מעבירים את ארגומנט הבדיקה --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), תוכלו להשתמש בקטעי הקוד הבאים כדי להגדיר את סביבת העבודה שלכם ב-Bazel עם רשימה של פריטי אספרסו נפוצים ומידת התלות שלהם:

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

אם אתם רוצים לבדוק את הביצועים מול כמה רמות 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]

בעיות ידועות

  • תהליכי ההעלאה של שרתי Forbed לא מסתיימים לאחר בדיקות
  • מבנה ה-APK פועל בכל הפלטפורמות (Linux, macOS, Windows) אבל הבדיקה פועלת רק ב-Linux.
  • גם עם --config=local_adb, משתמשים עדיין צריכים לציין android_instrumentation_test.target_device.
  • אם נעשה שימוש במכשיר מקומי או באמולטור, Bazel לא מסירה את ה-APKs לאחר הבדיקה. מנקים את החבילות באמצעות הפקודה הבאה:
adb shell pm list
packages com.example.android.testing | cut -d ':' -f 2 | tr -d '\r' | xargs
-L1 -t adb uninstall