تست های ابزار دقیق اندروید

اگر تازه وارد بازل هستید، با آموزش ساخت اندروید با بازل شروع کنید.

Running Android instrumentation tests in parallel

شکل 1. اجرای تست های موازی ابزار دقیق اندروید.

android_instrumentation_test به توسعه دهندگان این امکان را می دهد تا برنامه های خود را روی شبیه سازها و دستگاه های Android آزمایش کنند. از APIهای فریمورک اندروید واقعی و کتابخانه تست اندروید استفاده می کند.

برای هرمتیک بودن و تکرارپذیری، Bazel شبیه‌سازهای اندروید را در یک جعبه شنی ایجاد و راه‌اندازی می‌کند تا اطمینان حاصل کند که آزمایش‌ها همیشه از حالت تمیز اجرا می‌شوند. هر تست یک نمونه شبیه ساز جدا شده را دریافت می کند که به تست ها اجازه می دهد به صورت موازی بدون عبور از حالت های بین آنها اجرا شوند.

برای اطلاعات بیشتر در مورد تست‌های ابزار دقیق Android، مستندات برنامه‌نویس Android را بررسی کنید.

لطفاً مشکلات را در ردیاب مشکل GitHub ثبت کنید.

چگونه کار می کند

وقتی برای اولین بار bazel test روی هدف android_instrumentation_test اجرا می کنید، Bazel مراحل زیر را انجام می دهد:

  1. APK آزمایشی، APK تحت آزمایش و وابستگی‌های گذرا آنها را می‌سازد
  2. حالت های شبیه ساز تمیز را ایجاد، راه اندازی و ذخیره می کند
  3. شبیه ساز را راه اندازی می کند
  4. APK ها را نصب می کند
  5. با استفاده از Android Test Orchestrator تست ها را اجرا می کند
  6. شبیه ساز را خاموش می کند
  7. نتایج را گزارش می دهد

در اجرای آزمایشی بعدی، Bazel شبیه ساز را از حالت پاک و کش ایجاد شده در مرحله 2 بوت می کند، بنابراین هیچ حالت باقیمانده ای از اجرای قبلی وجود ندارد. حالت شبیه ساز کش نیز سرعت اجرای آزمایشی را افزایش می دهد.

پیش نیازها

اطمینان حاصل کنید که محیط شما پیش نیازهای زیر را برآورده می کند:

  • لینوکس . تست شده در اوبونتو 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
  • Xvfb . برای اجرای آزمایش‌های بدون سر (به عنوان مثال، روی سرورهای CI)، Bazel به فریم‌بافر مجازی X نیاز دارد.

برای نصب آن، اجرا کنید:

apt-get install xvfb

/usr/bin/Xvfb Xvfb :

which Xvfb

خروجی به صورت زیر است:

/usr/bin/Xvfb
  • کتابخانه های 32 بیتی برخی از باینری های استفاده شده توسط زیرساخت تست 32 بیتی هستند، بنابراین در ماشین های 64 بیتی، اطمینان حاصل کنید که باینری های 32 بیتی قابل اجرا هستند. برای اوبونتو، این کتابخانه های 32 بیتی را نصب کنید:
sudo apt-get install libc6:i386 libncurses5:i386 libstdc++6:i386 lib32z1 libbz2-1.0:i386

شروع شدن

در اینجا یک نمودار وابستگی هدف معمولی یک android_instrumentation_test است:

The target dependency graph on an 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 . این هدف مشخصات شبیه‌ساز اندروید را که Bazel برای ایجاد، راه‌اندازی و اجرای تست‌ها استفاده می‌کند، توصیف می‌کند. برای اطلاعات بیشتر به بخش انتخاب دستگاه اندروید مراجعه کنید.

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 : Android SDK. این را از طریق اندروید استودیو دانلود کنید.

  • @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 برای سطح API انتخاب‌شده مورد نیاز است. برای دانلود تصویر سیستم، از Android SDK's 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

تست رابط کاربری گرافیکی

اگر متغیر محیط $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 .

نمونه پروژه ها

اگر به دنبال نمونه‌های پروژه متعارف هستید، نمونه‌های تست اندروید برای پروژه‌های با استفاده از Espresso و UIAutomator را ببینید.

تنظیم اسپرسو

اگر تست‌های رابط کاربری را با اسپرسو می‌نویسید ( 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 اضافه کنید:

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]

مشکلات شناخته شده

  • فرآیندهای سرور adb فورک شده پس از آزمایش خاتمه نمی‌یابند
  • در حالی که ساختن APK روی همه پلتفرم ها (لینوکس، macOS، ویندوز) کار می کند، آزمایش فقط روی لینوکس کار می کند.
  • حتی با --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