Test Android apps (experimental)
Doc Detective can open and drive Android apps on a managed emulator as part of a test. This experimental workflow launches an app with startSurface, acts on it with the same find, click, and type steps you already use in the browser, and closes it with closeSurface.
Experimental: Native and mobile app testing is experimental and subject to change.
Before you begin
Android app tests run on an emulator that Doc Detective manages for you. The emulator needs hardware acceleration: KVM on Linux, HVF on macOS, or WHPX on Windows. A host without it can’t run the emulator. Doc Detective then skips the test with a reason rather than failing it, which keeps a mixed-platform spec green. Linux CI runners commonly expose KVM, which makes them the usual home for Android app tests. See Run on an Android emulator.
Android also needs the Android SDK toolchain. Install it ahead of time:
This installs the SDK, a system image, and the emulator. The download is multi-GB and opt-in, so it isn’t part of install all. On a capable host, the toolchain installs on demand the first time a run needs it, and the automation driver installs lazily on first use. See install android.
Run your first Android test
This test targets the android platform and launches the Settings app by its package name on the context’s default emulator. It opens a settings category, confirms the screen opened, and takes a screenshot of the window. It then returns to the top-level screen and closes the app. Settings ships with Android, so the test needs nothing else installed.
To run this test, save it to a spec file and pass it to Doc Detective:
A few things to notice:
- The generous
timeout(180,000 ms) leaves room for a cold emulator to boot and the app to launch. The toolchain and automation driver must be in place before the first step runs. Install them ahead of time withdoc-detective install android, or let a capable host install them lazily on the first run that needs them. waitUntil.findgates readiness. The step holds until the Network & internet entry exists, so the next step never runs against a screen that hasn’t drawn its controls yet. Gate readiness on an element the test actually uses, here the same entry the nextclicktargets. A screen title or heading can appear before the list below it has drawn. That would let the test race ahead to a control that isn’t there yet.- The
clicktargets the Network & internet entry, the Settings label on AOSP and Pixel images. Swap it for a category your device’s Settings app actually shows. If you change the category, also update thewaitUntil.findgate and the followingfind(hereInternet) to labels the new screens display. - The app that
startSurfaceopens becomes the active surface, so the later steps act on it with nosurfacefield. The example sets"name": "settings"onstartSurface. Pass an explicitsurfaceon a step, such as{ "app": "settings" }, to switch to a different surface. That switch sticks for the surface-less steps that follow.closeSurfacestill names the surface to close ({ "app": "settings" }) because it’s a lifecycle step, not an interaction on the active surface. If you omitname, Doc Detective derives the samesettingsdefault from the final dot-segment of the package name. Set it explicitly when you want a name of your own. See Target an app surface. - Beyond
click, an app surface takes the samefindandtypesteps you use in the browser. The example uses a standalonefindto confirm the category screen opened. It also uses atypestep that sends the$BACK$device key to return to the top level. For text entry,typesends text to the focused control on Android. Tap a field first, such as the Settings search box, and then type into it. - The
screenshotstep takes a fresh screenshot of the app every run, so the picture never drifts out of sync with the current build.
To test an app that isn’t already on the emulator, point install at an .apk and name the activity to launch:
See the app descriptor reference for the install and activity fields.
Choose the device or device type
By default, an Android app runs on the context’s default emulator, which Doc Detective creates on first use. To run on a specific device instead, set device on the context or the startSurface step: name a device, or refine it by deviceType (phone or tablet) and osVersion. See Choose the emulator and the device descriptor reference for the full list of fields.
Target only Android
Because an app targets one platform, gate the test with a context so it runs only where it can:
On a host that can’t run an Android emulator, the test skips with a reason rather than failing. See Test across platforms and browsers for the full context grammar.
Troubleshooting
Element not found
- Cause: Android exposes controls through UiAutomator2, so element criteria map onto its attributes.
elementTextmatches a widget’s text,elementIdandelementTestIdmatch its resource ID, andelementAriamatches its widget class, with the content description as the accessible name. A criterion that doesn’t line up with those attributes won’t match. - Solution: Match against the attribute that carries the value you want. Unlike Windows, macOS, and iOS, Android keeps text and content description as separate attributes.
elementTextandelementAriatherefore don’t collide, and you can set both on one step. See Target an app surface and thefindreference.
The app can’t reach a server on your machine
- Cause: The emulator is a separate device, so
localhostpoints at the emulator itself, not your host. - Solution: Reach a service on your host from the emulator at
10.0.2.2. See Mobile browsers (Android and iOS) for emulator networking.
A permission dialog blocks the test
- Cause: Android shows a runtime permission dialog that sits in front of the app.
- Solution: Treat the dialog as an element and click it (for example,
clickonelementText"Allow"), or suppress dialogs by settingdriverOptionsto{ "appium:autoGrantPermissions": true }on the surface. See Mobile app surfaces.
An element is off-screen
- Cause: The control you want isn’t in the visible viewport yet.
- Solution: Nothing extra: mobile
findauto-scrolls toward an off-screen element (up to five steps within the step’s timeout) before giving up.
Pressing a hardware key
- Cause: Android hardware keys (Back, Home, and so on) aren’t on-screen elements.
- Solution: Send a device-key token with
type:$BACK$,$HOME$,$APP_SWITCH$,$VOLUME_UP$, or$VOLUME_DOWN$.
An app or setting you added to the emulator is gone on the next run
- Cause: When Doc Detective boots the emulator, it resumes a clean snapshot, which also restores the device’s storage. Changes made after the snapshot don’t carry over.
- Solution: Run
doc-detective install android --reset-snapshot. The next boot retakes the snapshot with your change included. See How the emulator boots and the--reset-snapshotoption.
Performing a gesture
- Cause: Swipes and scrolls aren’t clicks.
- Solution: Use the
swipeaction, which drives Android gestures.
You can drive several apps in one test, because apps on one emulator share a single session. A later startSurface brings another app to the foreground on the same device and makes it the active surface. Pass an explicit surface on a step to act on the earlier app again.
What’s next
- Run on an Android emulator: host requirements and device management.
swipe: perform mobile gestures.- Device keys on mobile app surfaces: press hardware keys.
- Test across platforms and browsers: gate tests by platform.
- Inline tests and Detected tests: author these startSurface, click, screenshot, and closeSurface steps inline in a doc source file instead of a standalone spec.
- Docker and headless: run app tests in CI.