Android Enstrümantasyon Testleri

7.3 · 7.2 · 7.1 · 7.0 · 6.5

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

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

Şekil 1. Paralel Android araç testleri çalıştırma.

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

Bazel, hermetiklik ve yeniden üretilebilirlik için Android emülatörlerini bir korumalı alanda oluşturup başlatarak testlerin her zaman temiz bir durumda çalıştırılmasını sağlar. Her test için ayrı bir emülatör örneği oluşturulur. Bu sayede testler, aralarında durum aktarımı yapılmadan paralel olarak çalıştırılabilir.

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

Lütfen GitHub sorun izleyicisinde sorun bildirin.

İşleyiş şekli

bazel test öğesini 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 Orchestrator'u kullanarak testler çalıştırır
  6. Emülatörü kapatır
  7. Sonuçları raporlar

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ırmalarda kalan durumlar olmaz. Emülatör durumunu önbelleğe almak da test çalıştırma işlemlerini 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'te test edilmiştir.

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

bazel info release

Bu işlem, aşağıdakine benzer bir çıkış verir:

release 4.1.0

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

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

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

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

Yüklemek için şu komutu çalıştırın:

apt-get install xvfb

Aşağıdaki komutu çalıştırarak Xvfb'ün doğru şekilde ve /usr/bin/Xvfb konumuna yüklendiğini doğrulayın:

which Xvfb

Çıkış şu şekildedir:

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

Başlarken

Bir android_instrumentation_test'ün tipik hedef bağımlılık grafiği aşağıda verilmiştir:

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

Şekil 2. Bir android_instrumentation_test'ün 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 temel özellikleri şunlardır:

  • test_app: android_binary hedefi. Bu hedef, test kodu ve Espresso ile UIAutomator gibi bağımlılıkları içerir. Seçilen android_binary hedefinin, test edilen uygulama olan başka bir android_binary'ye işaret eden bir instruments özelliği belirtmesi gerekir.

  • target_device: android_device hedefi. Bu hedefte Bazel'in testleri oluşturmak, başlatmak ve çalıştırmak için kullandığı Android emülatörünün özellikleri açıklanmaktadır. Daha fazla bilgi için Android cihaz seçme bölümüne göz atın.

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

Test uygulaması için örnek bir AndroidTestManifest.xml aşağıda 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ı kullanabilmeniz için projenizin aşağıdaki harici depolara bağlı olması gerekir:

  • @androidsdk: Android SDK'sı. Bunu Android Studio'dan indirin.

  • @android_test_support: Test çalıştırıcısını, 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ümleyicisi kullanmanız gerekir.

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

android_device hedefi seçme

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

Örneğin, aşağıdakileri ç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, şuna 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üzeyinin system_image'ı gereklidir. Sistem görüntüsünü indirmek için Android SDK'sının tools/bin/sdkmanager 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 bölgesinde 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 hedefleri yerine QEMU2 android_device hedeflerini kullanın.

Test çalıştırma

Testleri ç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 aşağıdaki 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özetimli olmayan test

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

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

GUI testi

$DISPLAY ortam değişkeni ayarlandıysa test çalışırken emülatörün grafik arayüzünü etkinleştirmek mümkündür. Bunun için aşağıdaki test bağımsız değişkenlerini Bazel'e iletin:

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

Yerel bir emülatör veya cihazla test etme

Bazel ayrıca doğrudan yerel olarak başlatılan bir emülatörde veya bağlı cihazda test yapılmasını da destekler. Yerel test modunu etkinleştirmek için --test_strategy=exclusive ve --test_arg=--device_broker_type=LOCAL_ADB_SERVER işaretlerini iletin. 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 yazarsanız Bazel çalışma alanınızı yaygın olarak kullanılan Espresso yapıları ve bağımlılıklarıyla 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 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 project root/WORKSPACE komutuna gerekli bağımlılıkları 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 hedefinize //:test_deps bağımlılık 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 testlerin günlüklerini yazdırmak için --test_output=errors'ü, tüm test çıktılarını yazdırmak için --test_output=all'ü kullanın. Ayrı bir test günlüğü arıyorsanız $PROJECT_ROOT/bazel-testlogs/path/to/InstrumentationTestTargetName adresine gidin.

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

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

Bu işlem sonucunda aşağıdaki çıkış elde edilir:


$ 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 hedeflerine yönelik emülatör günlükleri, xxxxx'ın rastgele oluşturulmuş bir karakter dizisi olduğu emulator_xxxxx.log adlı /tmp/ dizininde saklanır.

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üzeyinde test etme

Birden fazla API düzeyinde test yapmak istiyorsanız her API düzeyi için test hedefleri oluşturmak üzere 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ışırken test yalnızca Linux'ta çalışır.
  • --config=local_adb olsa bile kullanıcıların android_instrumentation_test.target_device'i belirtmesi gerekir.
  • Yerel cihaz veya emülatör kullanılıyorsa Bazel, testten sonra APK'ları kaldırmaz. Aşağıdaki 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