Bazel을 처음 사용하는 경우 먼저 Bazel 튜토리얼
그림 1. 병렬 Android 계측 테스트 실행
android_instrumentation_test
을 사용하면 개발자가 Android 에뮬레이터와 기기에서 앱을 테스트할 수 있습니다.
실제 Android 프레임워크 API와 Android 테스트 라이브러리를 활용합니다.
밀폐성과 재현성을 위해 Bazel은 Android를 만들고 출시합니다. 에뮬레이터가 샌드박스에 저장되기 때문에 테스트가 항상 깨끗한 상태에서 실행되도록 합니다. 각 테스트가 격리된 에뮬레이터 인스턴스를 가져와 테스트를 병렬로 실행할 수 있습니다. 상태를 전달하지 않습니다.
Android 계측 테스트에 대한 자세한 내용은 Android 개발자 문서를 참조하세요.
GitHub Issue Tracker에서 문제를 신고해 주세요.
작동 방식
bazel test
를 android_instrumentation_test
타겟에서 실행하면
먼저 Bazel이 다음 단계를 수행합니다.
- 테스트 APK, 테스트 중인 APK, 전이 종속 항목을 빌드합니다.
- 클린 에뮬레이터 상태를 생성, 부팅, 캐시함
- 에뮬레이터 시작
- APK를 설치합니다.
- Android Test Orchestrator를 사용하여 테스트 실행
- 에뮬레이터 종료
- 결과 보고
후속 테스트 실행에서 Bazel은 캐시된 정리 상태에서 에뮬레이터를 부팅합니다. 생성되므로 이전 실행에서 남은 상태가 없습니다. 캐싱 에뮬레이터 상태는 테스트 실행 속도도 높여줍니다.
기본 요건
환경이 다음 기본 요건을 충족하는지 확인하세요.
Linux Ubuntu 16.04 및 18.04에서 테스트되었습니다.
Bazel 0.12.0 이상
bazel info release
를 실행하여 버전을 확인합니다.
bazel info release
그러면 다음과 비슷한 출력이 표시됩니다.
release 4.1.0
- KVM Bazel을 사용하려면 에뮬레이터에 하드웨어가 필요합니다. 가속도계 KVM을 사용하는 법을 배웠습니다 다음 설치 안내 Ubuntu용입니다
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
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
의 일반적인 타겟 종속 항목 그래프입니다.
그림 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 Emulator의 사양을 실행할 수 있습니다 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
: Android SDK입니다. Android 스튜디오를 통해 다운로드하세요.@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_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
를 실행하기 위해 선택한 API의 system_image
수준은 필수 항목입니다. 시스템 이미지를 다운로드하려면 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
QEMU
개가 아닌 android_device
타겟
테스트 실행
테스트를 실행하려면 다음 줄을 프로젝트의
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
는 다음 ID입니다.
adb devices
에 나열된 기기/에뮬레이터
샘플 프로젝트
표준 프로젝트 샘플을 찾고 있다면 Android 테스트 샘플 Espresso 및 UIAutomator를 사용하는 프로젝트에 적합합니다.
Espresso 설정
Espresso로 UI 테스트를 작성하는 경우
(androidx.test.espresso
)를 사용하는 경우, 다음 스니펫을 사용하여
일반적으로 사용되는 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_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 빌드는 모든 플랫폼 (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