blob: 0b063d6d7d503620830b42fca6f091dda9a43768 [file] [log] [blame] [view]
jingwen2f963272018-04-11 20:18:44 -07001---
2layout: documentation
3title: Android Instrumentation Tests
4---
5
6# Android Instrumentation Tests
7
8_If you're new to Bazel, please start with the [Building Android with
9Bazel](https://docs.bazel.build/versions/master/tutorial/android-app.html)
10tutorial._
11
12![Running Android instrumentation tests in parallel](/assets/android_test.gif)
13
14[`android_instrumentation_test`](https://docs.bazel.build/versions/master/be/android.html#android_instrumentation_test)
15allows developers to test their apps on Android emulators and devices.
16It utilizes real Android framework APIs and the Android Test Library.
17
18For hermeticity and reproducibility, Bazel creates and launches Android
19emulators in a sandbox, ensuring that tests always run from a clean state. Each
20test gets an isolated emulator instance, allowing tests to run in parallel
21without passing states between them.
22
23For more information on Android instrumentation tests, check out the [Android
24developer
25documentation](https://developer.android.com/training/testing/unit-testing/instrumented-unit-tests.html).
26
27The current state is __experimental__ as of Bazel 0.12.0. Please file issues in
28the [GitHub issue tracker](https://github.com/bazelbuild/bazel/issues).
29
30**Table of Contents**
31
32- [How it works](#how-it-works)
33- [Prerequisites](#prerequisites)
34- [Getting started](#getting-started)
35 - [`BUILD` file](#build-file)
36 - [`WORKSPACE` dependencies](#workspace-dependencies)
37- [Maven dependencies](#maven-dependencies)
38- [Choosing an `android_device` target](#choosing-an-android_device-target)
39- [Running tests](#running-tests)
40 - [Headless testing](#headless-testing)
41 - [GUI testing](#gui-testing)
42 - [Testing with a local emulator or device](#testing-with-a-local-emulator-or-device)
43- [Sample projects](#sample-projects)
44- [Tips](#tips)
45 - [Reading test logs](#reading-test-logs)
46 - [Testing against multiple API levels](#testing-against-multiple-api-levels)
47- [Known issues](#known-issues)
48- [Planned features](#planned-features)
49
50# How it works
51
52When you run `bazel test` on an `android_instrumentation_test` target for the
53first time, Bazel performs the following steps:
54
551. Builds the test APK, APK under test, and their transitive dependencies
562. Creates, boots, and caches clean emulator states
573. Starts the emulator
584. Installs the APKs
595. Runs tests utilizing the [Android Test Orchestrator](https://developer.android.com/training/testing/junit-runner.html#using-android-test-orchestrator)
606. Shuts down the emulator
617. Reports the results
62
63In subsequent test runs, Bazel boots the emulator from the clean, cached state
64created in step 2, so there are no leftover states from previous runs. Caching
65emulator state also speeds up test runs.
66
67# Prerequisites
68
69Ensure your enivornment satisfies the following prerequisites:
70
71- **Linux**. Tested on Ubuntu 14.04 and 16.04.
72
73- **Bazel 0.12.0** or later. Verify the version by running `bazel info release`.
74
75```
76$ bazel info release
77release 0.12.0
78```
79
80- **KVM**. Bazel requires emulators to have [hardware
81 acceleration](https://developer.android.com/studio/run/emulator-acceleration.html#accel-check)
82 with KVM on Linux. You can follow these
83 [installation instructions](https://help.ubuntu.com/community/KVM/Installation)
84 for Ubuntu. Run `apt-get install cpu-checker && kvm-ok` to verify that KVM has
85 the correct configuration. If it prints the following message, you're good to
86 go:
87
88```
89$ kvm-ok
90INFO: /dev/kvm exists
91KVM acceleration can be used
92```
93
94- **Xvfb**. To run headless tests (for example, on CI servers), Bazel requires
95 the [X virtual framebuffer](https://www.x.org/archive/X11R7.6/doc/man/man1/Xvfb.1.xhtml).
96 Install it by running `apt-get install xvfb`. Verify that `Xvfb` is installed
97 correctly by running `which Xvfb` and ensure that it's installed at
98 `/usr/bin/Xvfb`:
99
100```
101$ which Xvfb
102/usr/bin/Xvfb
103```
104
jingwen2f963272018-04-11 20:18:44 -0700105# Getting started
106
107Here is a typical target dependency graph of an `android_instrumentation_test`:
108
109![The target dependency graph on an Android instrumentation test](/assets/android_instrumentation_test.png)
110
111## `BUILD` file
112
113The graph translates into a `BUILD` file like this:
114
115```python
116load("@gmaven_rules//:defs.bzl", "gmaven_artifact")
117
118android_instrumentation_test(
119 name = "my_test",
120 test_app = ":my_test_app",
121 target_device = "@android_test_support//tools/android/emulated_devices/generic_phone:android_23_x86_qemu2",
122)
123
124# Test app and library
125android_binary(
126 name = "my_test_app",
127 instruments = ":my_app",
128 manifest = "AndroidTestManifest.xml",
129 deps = [":my_test_lib"],
130 # ...
131)
132
133android_library(
134 name = "my_test_lib",
135 srcs = glob(["javatest/**/*.java"]),
136 deps = [
137 ":my_app_lib",
138 gmaven_artifact("com.android.support.test.espresso:espresso_core:aar:3.0.1"),
139 ],
140 # ...
141)
142
143# Target app and library under test
144android_binary(
145 name = "my_app",
146 manifest = "AndroidManifest.xml",
147 deps = [":my_app_lib"],
148 # ...
149)
150
151android_library(
152 name = "my_app_lib",
153 srcs = glob(["java/**/*.java"]),
154 deps = [
155 gmaven_artifact("com.android.support:design:aar:27.0.2"),
156 gmaven_artifact("com.android.support:support_annotations:jar:27.0.2"),
157 ]
158 # ...
159)
160```
161
162The main attributes of the rule `android_instrumentation_test` are:
163
164- `test_app`: An `android_binary` target. This target contains test code and
165 dependencies like Espresso and UIAutomator. The selected `android_binary`
166 target is required to specify an `instruments` attribute pointing to another
167 `android_binary`, which is the app under test.
168
169- `target_device`: An `android_device` target. This target describes the
170 specifications of the Android emulator which Bazel uses to create, launch and
171 run the tests. See the [section on choosing an Android
172 device](#choosing-an-android_device) for more information.
173
174## `WORKSPACE` dependencies
175
176In order to use this rule, your project needs to depend on these external
177repositories:
178
179- `@androidsdk`: The Android SDK. Download this through Android Studio.
180
181- `@android_test_support`: Hosts the test runner, emulator launcher, and
182 `android_device` targets.
183
184- `@gmaven_rules`: Defines the `maven_jar` and `maven_aar` targets available on
185 the [Google Maven repository](https://maven.google.com).
186
187You can enable these dependencies by adding the following lines to your
188`WORKSPACE` file:
189
190```python
191# Android SDK
192android_sdk_repository(
193 name = "androidsdk",
194 path = "/path/to/sdk", # or set ANDROID_HOME
195)
196
197# Android Test Support
198ATS_COMMIT = "$COMMIT_HASH"
199http_archive(
200 name = "android_test_support",
jingwenc251ead2018-05-16 15:07:25 -0700201 strip_prefix = "android-test-%s" % ATS_COMMIT",
202 urls = ["https://github.com/android/android-test/archive/%s.tar.gz" % ATS_COMMIT],
jingwen2f963272018-04-11 20:18:44 -0700203)
204load("@android_test_support//:repo.bzl", "android_test_repositories")
205android_test_repositories()
206
207# Google Maven Repository
jingwena5876cd2018-04-16 09:05:05 -0700208GMAVEN_TAG = "0.1.0"
jingwen2f963272018-04-11 20:18:44 -0700209http_archive(
210 name = "gmaven_rules",
jingwena5876cd2018-04-16 09:05:05 -0700211 strip_prefix = "gmaven_rules-%s" % GMAVEN_TAG,
212 urls = ["https://github.com/bazelbuild/gmaven_rules/archive/%s.tar.gz" % GMAVEN_TAG],
jingwen2f963272018-04-11 20:18:44 -0700213)
214load("@gmaven_rules//:gmaven.bzl", "gmaven_rules")
215gmaven_rules()
216```
217
218# Maven dependencies
219
220Use the
221[maven_jar](https://docs.bazel.build/versions/master/be/workspace.html#maven_jar)
222repository rule for Maven dependencies not hosted on Google Maven. For example,
223to use JUnit 4.12 and Hamcrest 2, add the following lines to your `WORKSPACE`:
224
225```
226maven_jar(
227 name = "junit_junit",
228 artifact = "junit:junit:4.12",
229)
230
231maven_jar(
232 name = "org_hamcrest_java_hamcrest",
233 artifact = "org.hamcrest:java-hamcrest:2.0.0.0",
234)
235```
236
237Then, you can depend on them in your `BUILD` files:
238
239```python
240java_library(
241 name = "test_deps",
242 visibility = ["//visibility:public"],
243 exports = [
244 "@junit_junit//jar",
245 "@org_hamcrest_java_hamcrest//jar",
246 ],
247)
248
249android_library(
250 name = "my_test_lib",
251 srcs = [..],
252 deps = [":test_deps"],
253)
254```
255
256[`bazel-deps`](https://github.com/johnynek/bazel-deps) is another useful tool
257for managing Maven dependencies using [a `YAML`
258file](https://github.com/johnynek/bazel-deps/blob/master/dependencies.yaml).
259
jingwena5876cd2018-04-16 09:05:05 -0700260For dependencies hosted on [Google's Maven
jingwen2f963272018-04-11 20:18:44 -0700261repository](https://maven.google.com), [`@gmaven_rules`](https://github.com/bazelbuild/gmaven_rules)
262provides a simple way to fetch dependencies hosted with `gmaven_artifact`.
263
264`gmaven_artifact` is a macro that maps an artifact's coordinate to the actual
265generated target in
266[`gmaven.bzl`](https://raw.githubusercontent.com/bazelbuild/gmaven_rules/master/gmaven.bzl)
267(warning: big file!). The packaging type defaults to `jar` if it isn't
268specified.
269
270Load the `gmaven_artifact` macro at the beginning of your `BUILD` file to use
271it:
272
273```python
274load("@gmaven_rules//:defs.bzl", "gmaven_artifact")
275
276android_library(
277 name = "my_app_lib",
278 srcs = glob(["java/**/*.java"]),
279 deps = [
280 gmaven_artifact("com.android.support:design:aar:27.0.2"),
281 gmaven_artifact("com.android.support:support_annotations:jar:27.0.2"),
282 ]
283 # ...
284)
285```
286
287# Choosing an android_device target
288
289`android_instrumentation_test.target_device` specifies which Android device to
290run the tests on. These `android_device` targets are defined in
291[`@android_test_support`](https://github.com/google/android-testing-support-library/tree/master/tools/android/emulated_devices).
292
293```python
294$ bazel query --output=build @android_test_support//tools/android/emulated_devices/generic_phone:android_23_x86_qemu2
295# .../external/android_test_support/tools/android/emulated_devices/generic_phone/BUILD:43:1
296android_device(
297 name = "android_23_x86_qemu2",
298 visibility = ["//visibility:public"],
299 tags = ["requires-kvm"],
300 generator_name = "generic_phone",
301 generator_function = "make_device",
302 generator_location = "tools/android/emulated_devices/generic_phone/BUILD:43",
303 vertical_resolution = 800,
304 horizontal_resolution = 480,
305 ram = 2048,
306 screen_density = 240,
307 cache = 32,
308 vm_heap = 256,
309 system_image = "@android_test_support//tools/android/emulated_devices/generic_phone:android_23_x86_qemu2_images",
310 default_properties = "@android_test_support//tools/android/emulated_devices/generic_phone:_android_23_x86_qemu2_props",
311)
312```
313
314The device target names use this template:
315
316```
317@android_test_support//tools/android/emulated_devices/${device_type}:${system}_${api_level}_x86_qemu2
318```
319
320In order to launch an `android_device`, the `system_image` for the selected API
321level is required. To download the system image, use Android SDK's
322`tools/bin/sdkmanager`. For example, to download the system image for
323`generic_phone:android_23_x86_qemu2`, run `$sdk/tools/bin/sdkmanager
324"system-images;android-23;default;x86"`.
325
326To see the full list of supported `android_device` targets in
327`@android_test_support`, run the following command:
328
329```
330bazel query 'filter("x86_qemu2$", kind(android_device, @android_test_support//tools/android/emulated_devices/...:*))'
331```
332
333Bazel currently supports x86-based emulators only. For better performance,
334we also recommend using `QEMU2` `android_device` targets instead of `QEMU` ones.
335
336# Running tests
337
338To run tests, add these lines to your project's `tools/bazel.rc` file.
339
340```
341# Configurations for testing with Bazel
342# Select a configuration by running
343# `bazel test //my:target --config={headless, gui, local_device}`
344
345# Headless instrumentation tests
jingwen2f963272018-04-11 20:18:44 -0700346test:headless --test_arg=--enable_display=false
347
348# Graphical instrumentation tests. Ensure that $DISPLAY is set.
349test:gui --test_env=DISPLAY
350test:gui --test_arg=--enable_display=true
351
352# Testing with a local emulator or device. Ensure that `adb devices` lists the
353# device.
354# Run tests serially.
355test:local_device --test_strategy=exclusive
356# Use the local device broker type, as opposed to WRAPPED_EMULATOR.
357test:local_device --test_arg=--device_broker_type=LOCAL_ADB_SERVER
358# Uncomment and set $device_id if there is more than one connected device.
359# test:local_device --test_arg=--device_serial_number=$device_id
360```
361
362Then, use one of the configurations to run tests:
363
364- `bazel test //my/test:target --config=headless`
365- `bazel test //my/test:target --config=gui`
366- `bazel test //my/test:target --config=local_device`
367
368Use __only one configuration__ or tests will fail.
369
370## Headless testing
371
jingwenb083de82018-04-30 07:53:09 -0700372With `Xvfb`, it is possible to test with emulators without the graphical
373interface, also known as headless testing. To disable the graphical interface
374when running tests, pass the test argument `--enable_display=false` to Bazel:
375
376```
377bazel test //my/test:target --test_arg=--enable_display=false
378```
jingwen2f963272018-04-11 20:18:44 -0700379
380## GUI testing
381
jingwenb083de82018-04-30 07:53:09 -0700382If the `$DISPLAY` environment variable is set, it's possible to enable the
383graphical interface of the emulator while the test is running. To do this, pass
384these test arguments to Bazel:
385
386```
387bazel test //my/test:target --test_arg=--enable_display --test_env=DISPLAY
388```
jingwen2f963272018-04-11 20:18:44 -0700389
390## Testing with a local emulator or device
391
392Bazel also supports testing directly on a locally launched emulator or connected
393device. Pass the flags
394`--test_strategy=exclusive` and
395`--test_arg=--device_broker_type=LOCAL_ADB_SERVER` to enable local testing mode.
396If there is more than one connected device, pass the flag
397`--test_arg=--device_serial_number=$device_id` where `$device_id` is the id of
398the device/emulator listed in `adb devices`.
399
400# Sample projects
401
402If you are looking for canonical project samples, see the [Android testing
403samples](https://github.com/googlesamples/android-testing#experimental-bazel-support)
404for projects using Espresso and UIAutomator.
405
406```
407$ git clone https://github.com/googlesamples/android-testing && cd android-testing
408# Set path to Android SDK in WORKSPACE
409$ bazel test //ui/... --config=headless
410INFO: Analysed 45 targets (1 packages loaded).
411INFO: Found 36 targets and 9 test targets...
412
413...
414
415INFO: Elapsed time: 195.665s, Critical Path: 195.22s
416INFO: Build completed successfully, 417 total actions
417//ui/espresso/BasicSample:BasicSampleInstrumentationTest PASSED in 103.7s
418//ui/espresso/CustomMatcherSample:CustomMatcherSampleInstrumentationTest PASSED in 113.2s
419//ui/espresso/DataAdapterSample:DataAdapterSampleInstrumentationTest PASSED in 110.2s
420//ui/espresso/IdlingResourceSample:IdlingResourceSampleInstrumentationTest PASSED in 102.3s
421//ui/espresso/IntentsAdvancedSample:IntentsAdvancedSampleInstrumentationTest PASSED in 98.3s
422//ui/espresso/IntentsBasicSample:IntentsBasicSampleInstrumentationTest PASSED in 103.3s
423//ui/espresso/MultiWindowSample:MultiWindowSampleInstrumentationTest PASSED in 108.3s
424//ui/espresso/RecyclerViewSample:RecyclerViewSampleInstrumentationTest PASSED in 102.9s
425//ui/uiautomator/BasicSample:BasicSampleInstrumentationTest PASSED in 122.6s
426```
427
428# Tips
429
430## Reading test logs
431
432Use `--test_output=errors` to print logs for failing tests, or
433`--test_output=all` to print all test output. If you're looking for an
434individual test log, go to
435`$PROJECT_ROOT/bazel-testlogs/path/to/InstrumentationTestTargetName`.
436
437For example, the test logs for `BasicSample` canonical project are in
438`bazel-testlogs/ui/espresso/BasicSample/BasicSampleInstrumentationTest`:
439
440```
441$ tree bazel-testlogs/ui/espresso/BasicSample/BasicSampleInstrumentationTest
442.
443├── adb.409923.log
444├── broker_logs
445│   ├── aapt_binary.10.ok.txt
446│   ├── aapt_binary.11.ok.txt
447│   ├── adb.12.ok.txt
448│   ├── adb.13.ok.txt
449│   ├── adb.14.ok.txt
450│   ├── adb.15.fail.txt
451│   ├── adb.16.ok.txt
452│   ├── adb.17.fail.txt
453│   ├── adb.18.ok.txt
454│   ├── adb.19.fail.txt
455│   ├── adb.20.ok.txt
456│   ├── adb.21.ok.txt
457│   ├── adb.22.ok.txt
458│   ├── adb.23.ok.txt
459│   ├── adb.24.fail.txt
460│   ├── adb.25.ok.txt
461│   ├── adb.26.fail.txt
462│   ├── adb.27.ok.txt
463│   ├── adb.28.fail.txt
464│   ├── adb.29.ok.txt
465│   ├── adb.2.ok.txt
466│   ├── adb.30.ok.txt
467│   ├── adb.3.ok.txt
468│   ├── adb.4.ok.txt
469│   ├── adb.5.ok.txt
470│   ├── adb.6.ok.txt
471│   ├── adb.7.ok.txt
472│   ├── adb.8.ok.txt
473│   ├── adb.9.ok.txt
474│   ├── android_23_x86_qemu2.1.ok.txt
475│   └── exec-1
476│   ├── adb-2.txt
477│   ├── emulator-2.txt
478│   └── mksdcard-1.txt
479├── device_logcat
480│   └── logcat1635880625641751077.txt
481├── emulator_itCqtc.log
482├── outputs.zip
483├── pipe.log.txt
484├── telnet_pipe.log.txt
485└── tmpuRh4cy
486 ├── watchdog.err
487 └── watchdog.out
488
4894 directories, 41 files
490```
491
jingwenb083de82018-04-30 07:53:09 -0700492## Reading emulator logs
493
494The emulator logs for `android_device` targets are stored in the `/tmp/`
495directory with the name `emulator_xxxxx.log`, where `xxxxx` is a
496randomly-generated sequence of characters.
497
498Use this command to find the latest emulator log:
499
500```
501ls -1t /tmp/emulator_*.log | head -n 1
502```
503
jingwen2f963272018-04-11 20:18:44 -0700504## Testing against multiple API levels
505
506If you would like to test against multiple API levels, you can use a list
507comprehension to create test targets for each API level. For example:
508
509```python
510API_LEVELS = [
511 "19",
512 "20",
513 "21",
514 "22",
515]
516
517[android_instrumentation_test(
518 name = "my_test_%s" % API_LEVEL,
519 test_app = ":my_test_app",
520 target_device = "@android_test_support//tools/android/emulated_devices/generic_phone:android_%s_x86_qemu2" % API_LEVEL,
521) for API_LEVEL in API_LEVELS]
522```
523
524# Known issues
525
526- [Forked adb server processes are not terminated after
527 tests](https://github.com/bazelbuild/bazel/issues/4853)
jingwen2f963272018-04-11 20:18:44 -0700528- While APK building works on all platforms (Linux, macOS, Windows), testing
529 only works on Linux.
530- Even with `--config=local_adb`, users still need to specify
531 `android_instrumentation_test.target_device`.
532- If using a local device or emulator, Bazel does not uninstall the APKs after
533 the test. Clean the packages by running this command: `adb shell pm list
534 packages com.example.android.testing | cut -d ':' -f 2 | tr -d '\r' | xargs
535 -L1 -t adb uninstall`
536
537# Planned features
538
jingwen2f963272018-04-11 20:18:44 -0700539- Code coverage collection
540- macOS support
541- Windows support
542- Improved external dependency management
543- Remote test caching and execution
Jingwen Chenb8a71842018-04-12 10:22:30 -0700544
545We are planning to rewrite the Android rules in [Skylark](https://docs.bazel.build/versions/master/skylark/concepts.html).
546The `android_instrumentation_test` rule will be part of the rewrite, however,
547its usage will remain unchanged from the end-user perspective.