Android Enstrümantasyon Testleri

Sorun bildir Kaynağı göster

Bazel'i kullanmaya yeni başladıysanız Bazel ile Android oluşturma eğiticisiyle başlayın.

Android araç testlerini paralel olarak çalıştırma

Şekil 1. Paralel Android enstrümantasyon testleri çalıştırma.

android_instrumentation_test, geliştiricilerin uygulamalarını Android emülatörlerinde ve cihazlarda test etmesine olanak tanır. Gerçek Android çerçevesi API'lerini ve Android Test Kitaplığı'nı kullanır.

Hermetik ve yeniden üretilebilirlik için Bazel, Android emülatörleri oluşturup bunları korumalı alanda kullanıma sunarak testlerin her zaman temiz bir durumda çalışmasını sağlar. Her test izole bir emülatör örneği alır. Bu sayede, testler aralarında durum geçirmeden paralel olarak yürütülebilir.

Android enstrümantasyon testleri hakkında daha fazla bilgi için Android geliştirici belgelerine göz atın.

Lütfen sorunları GitHub sorun izleyicisine gönderin.

İşleyiş şekli

bazel test öğesini bir android_instrumentation_test hedefinde ilk kez çalıştırdığınızda, Bazel aşağıdaki adımları gerçekleştirir:

  1. Test APK'sını, test edilen APK'yı ve bunların geçişli bağımlılıklarını oluşturur
  2. Temiz emülatör durumları oluşturur, başlatır ve önbelleğe alır
  3. Emülatörü başlatır
  4. APK'ları yükler
  5. Android Test Orkestratörü'nü kullanarak testler çalıştırır.
  6. Emülatörü kapatır
  7. Sonuçları bildirir

Sonraki test çalıştırmalarında Bazel, emülatörü 2. adımda oluşturulan temiz, önbelleğe alınmış durumdan başlatır. Böylece, önceki çalıştırmalardan kalan durumlar olmaz. Emülatör durumunu önbelleğe almak, test çalıştırmalarını da hızlandırır.

Ön koşullar

Ortamınızın aşağıdaki ön koşulları karşıladığından emin olun:

  • Linux. Ubuntu 16.04 ve 18.04 sürümlerinde test edilmiştir.

  • Bazel 0.12.0 veya sonraki sürümler. bazel info release eklentisini çalıştırarak sürümü doğrulayın.

bazel info release

Bu, aşağıdakine benzer bir çıkışla sonuçlanır:

release 4.1.0

KVM'nin doğru yapılandırmaya sahip olduğunu doğrulamak için şu komutu çalıştırın:

apt-get install cpu-checker && kvm-ok

Aşağıdaki iletiyi yazdırıyorsa doğru yapılandırmaya sahipsiniz demektir:

INFO: /dev/kvm exists
KVM acceleration can be used

Bu uygulamayı yüklemek için şu komutu çalıştırın:

apt-get install xvfb

Xvfb öğesinin doğru şekilde yüklendiğini ve /usr/bin/Xvfb adresine yüklendiğini doğrulamak için aşağıdaki adımları uygulayın:

which Xvfb

Çıkış şu şekildedir:

/usr/bin/Xvfb
  • 32 bit Kitaplıklar. Test altyapısı tarafından kullanılan bazı ikili programlar 32 bit olduğundan 64 bit makinelerde 32 bit ikili programların çalıştırılabilmesini sağlar. Ubuntu için aşağıdaki 32 bit kitaplıkları yükleyin:
sudo apt-get install libc6:i386 libncurses5:i386 libstdc++6:i386 lib32z1 libbz2-1.0:i386

Kullanmaya başlama

android_instrumentation_test tipik bir hedef bağımlılık grafiğidir:

Bir Android enstrümantasyon testindeki hedef bağımlılık grafiği

2. Şekil. Bir android_instrumentation_test hedef bağımlılık grafiği.

BUILD dosyası

Grafik aşağıdaki gibi bir BUILD dosyasına dönüştürülür:

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 kuralının ana özellikleri şunlardır:

  • test_app: android_binary hedefi. Bu hedef, test kodunu ve Espresso ile UIAutomator gibi bağımlılıkları içerir. Seçilen android_binary hedefi, test edilen uygulama olan başka bir android_binary öğesine işaret eden bir instruments özelliği belirtmek için gereklidir.

  • target_device: android_device hedefi. Bu hedef, Bazel'in testleri oluşturmak, başlatmak ve çalıştırmak için kullandığı Android emülatörünün özelliklerini açıklar. Daha fazla bilgi için Android cihaz seçmeyle ilgili bölüme bakın.

Test uygulamasının AndroidManifest.xml özelliği <instrumentation> etiketi içermelidir. Bu etiket, hedef uygulama paketi için özellikleri ve araç test çalıştırıcısının tam sınıf adını, androidx.test.runner.AndroidJUnitRunner belirtmelidir.

Aşağıda, test uygulaması için bir örnek AndroidTestManifest.xml verilmiştir:

<?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 bağımlılıkları

Bu kuralı kullanmak için projenizin şu harici depolara bağlı olması gerekir:

  • @androidsdk: Android SDK'sı. Bu içeriği Android Studio'dan indirin.

  • @android_test_support: Test çalıştırıcıyı, emülatör başlatıcıyı ve android_device hedeflerini barındırır. En son sürümü burada bulabilirsiniz.

Aşağıdaki satırları WORKSPACE dosyanıza ekleyerek bu bağımlılıkları etkinleştirin:

# 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 bağımlılıkları

Google Maven veya Maven Central gibi depolardaki Maven yapılarına olan bağımlılıkları yönetmek için rules_jvm_external gibi bir Maven çözümleyici kullanmanız gerekir.

Bu sayfanın geri kalanında, Maven depolarındaki bağımlılıkları çözmek ve getirmek için rules_jvm_external özelliğinin nasıl kullanılacağı gösterilmektedir.

android_device hedefi seçme

android_instrumentation_test.target_device, testlerin hangi Android cihaz üzerinde çalıştırılacağını belirtir. Bu android_device hedef @android_test_support içinde tanımlanmıştır.

Örneğin, aşağıdaki komutu çalıştırarak belirli bir hedefin kaynaklarını sorgulayabilirsiniz:

bazel query --output=build @android_test_support//tools/android/emulated_devices/generic_phone:android_23_x86

Bu da aşağıdakine benzer bir çıkışla sonuçlanır:

# .../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",
)

Cihaz hedefi adları şu şablonu kullanır:

@android_test_support//tools/android/emulated_devices/device_type:system_api_level_x86_qemu2

Bir android_device başlatmak için seçilen API düzeyinde system_image gereklidir. Sistem görüntüsünü indirmek için Android SDK'sının tools/bin/sdkmanager özelliğini kullanın. Örneğin, generic_phone:android_23_x86 için sistem görüntüsünü indirmek üzere $sdk/tools/bin/sdkmanager "system-images;android-23;default;x86" komutunu çalıştırın.

@android_test_support ürününde desteklenen android_device hedeflerinin tam listesini görmek için aşağıdaki komutu çalıştırın:

bazel query 'filter("x86_qemu2$", kind(android_device, @android_test_support//tools/android/emulated_devices/...:*))'

Bazel şu anda yalnızca x86 tabanlı emülatörleri desteklemektedir. Daha iyi performans için QEMU hedef yerine QEMU2 android_device hedefleri kullanın.

Test çalıştırma

Test çalıştırmak için bu satırları projenizin project root:/.bazelrc dosyasına ekleyin.

# 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

Ardından, test çalıştırmak için yapılandırmalardan birini kullanın:

  • bazel test //my/test:target --config=gui
  • bazel test //my/test:target --config=headless
  • bazel test //my/test:target --config=local_device

Yalnızca bir yapılandırma kullanın. Aksi takdirde testler başarısız olur.

Gözetimsiz test

Xvfb ile, grafik arayüz olmadan, gözetimsiz test olarak da bilinen emülatörlerle test yapmak mümkündür. Test çalıştırırken grafik arayüzü devre dışı bırakmak için --enable_display=false test bağımsız değişkenini Bazel'e iletin:

bazel test //my/test:target --test_arg=--enable_display=false

GUI testi

$DISPLAY ortam değişkeni ayarlanırsa test çalışırken emülatörün grafik arayüzü etkinleştirilebilir. Bunu yapmak için Bazel'a şu test argümanlarını iletin:

bazel test //my/test:target --test_arg=--enable_display=true --test_env=DISPLAY

Yerel bir emülatör veya cihazla test etme

Bazel, doğrudan yerel olarak başlatılan emülatörde veya bağlı cihazda test etmeyi de destekler. Yerel test modunu etkinleştirmek için --test_strategy=exclusive ve --test_arg=--device_broker_type=LOCAL_ADB_SERVER işaretlerini geçin. Birden fazla bağlı cihaz varsa --test_arg=--device_serial_number=$device_id işaretini iletin. Burada $device_id, adb devices içinde listelenen cihazın/emülatörün kimliğidir.

Örnek projeler

Standart proje örnekleri arıyorsanız Espresso ve UIAutomator kullanan projeler için Android test örneklerine bakın.

Espresso kurulumu

Espresso (androidx.test.espresso) ile kullanıcı arayüzü testleri yazıyorsanız Bazel çalışma alanınızı yaygın olarak kullanılan Espresso yapılarının ve bunların bağımlılıklarının listesiyle ayarlamak için aşağıdaki snippet'leri kullanabilirsiniz:

androidx.test.espresso:espresso-core
androidx.test:rules
androidx.test:runner
javax.inject:javax.inject
org.hamcrest:java-hamcrest
junit:junit

Bu bağımlılıkları düzenlemenin bir yolu, project root/BUILD.bazel dosyanızda //:test_deps adlı bir paylaşılan kitaplık oluşturmaktır:

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",
    ],
)

Ardından, gerekli bağımlılıkları project root/WORKSPACE bölümüne ekleyin:

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",
    ],
)

Son olarak, test android_binary hedefinizde //:test_deps bağımlılığını ekleyin:

android_binary(
    name = "my_test_app",
    instruments = "//path/to:app",
    deps = [
        "//:test_deps",
        # ...
    ],
    # ...
)

İpuçları

Test günlüklerini okuma

Başarısız olan testlerin günlüklerini yazdırmak için --test_output=errors, tüm test çıkışlarını yazdırmak için --test_output=all kullanın. Bağımsız bir test günlüğü arıyorsanız $PROJECT_ROOT/bazel-testlogs/path/to/InstrumentationTestTargetName bölümüne gidin.

Örneğin, BasicSample standart projesinin test günlükleri bazel-testlogs/ui/espresso/BasicSample/BasicSampleInstrumentationTest içindedir, şu komutu çalıştırın:

tree bazel-testlogs/ui/espresso/BasicSample/BasicSampleInstrumentationTest

Bu, aşağıdaki çıkışla sonuçlanır:


$ 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

Emülatör günlüklerini okuma

android_device hedeflerinin emülatör günlükleri, /tmp/ dizininde emulator_xxxxx.log adıyla depolanır. Burada xxxxx, rastgele oluşturulan bir karakter dizisidir.

En son emülatör günlüğünü bulmak için şu komutu kullanın:

ls -1t /tmp/emulator_*.log | head -n 1

Birden fazla API düzeyiyle test etme

Birden çok API düzeyinde test yapmak istiyorsanız her API düzeyi için test hedefleri oluşturmak amacıyla bir liste anlama özelliğini kullanabilirsiniz. Örneğin:

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]

Bilinen sorunlar

  • Çatallı adb sunucusu işlemleri testlerden sonra sonlandırılmıyor
  • APK oluşturma tüm platformlarda (Linux, macOS, Windows) çalışsa da testler yalnızca Linux'ta yapılır.
  • --config=local_adb kullanılsa bile kullanıcıların android_instrumentation_test.target_device alanını belirtmesi gerekir.
  • Yerel bir cihaz veya emülatör kullanıyorsanız Bazel, testten sonra APK'ların yüklemesini kaldırmaz. Şu komutu çalıştırarak paketleri temizleyin:
adb shell pm list
packages com.example.android.testing | cut -d ':' -f 2 | tr -d '\r' | xargs
-L1 -t adb uninstall