Skip to main content

Local Emulator Setup

Tier 1 — runs Appium on the developer's macOS machine against a local Android Emulator. Zero cost, instant feedback, single-developer use.

This is the easiest path to a working mobile test, and what every demo uses.

Why "native macOS, not Docker"

Docker Desktop on macOS can't run Android Emulators — no KVM / nested virtualization on the macOS hypervisor. The platform's backend services run in Docker, but Appium and the emulator must run natively on the host.

The mobile-test-agent also runs natively on macOS (as a JAR) so it can talk to local Appium without crossing the Docker NAT.

┌──────────────────────────── Docker (backend) ─────────────────────────┐
│ proofarc-backend │ proofarc-postgres │ proofarc-ui │ proofarc-minio │
└──────────────────────────────────┬─────────────────────────────────────┘
│ HTTP / WebSocket

┌──────────────────────────── macOS host ───────────────────────────────┐
│ mobile-test-agent (Java JAR) │
│ │ │
│ ▼ WebDriver to localhost:4723 │
│ Appium server ────► Android Emulator (Pixel_7_API_34) │
└────────────────────────────────────────────────────────────────────────┘

Prerequisites

ToolInstall
Java 17+brew install openjdk@17
Node.jsbrew install node
Docker Desktopfrom docker.com (for backend services)
Android SDKAndroid Studio, OR run ./scripts/install-android-sdk.sh

The script installs adb, emulator, an API 34 system image, and creates a Pixel_7_API_34 AVD without needing Android Studio.

One-shot setup

From the repo root:

./scripts/start-mobile-env.sh local

This runs the full stack:

  1. Verifies prerequisites (Java, Node, Android SDK)
  2. Installs Appium + the uiautomator2 driver if missing
  3. Boots the Android Emulator (Pixel_7_API_34)
  4. Starts the Appium server on port 4723
  5. Starts the Docker backend services
  6. Builds and runs the mobile-test-agent JAR locally

When it's done, visit http://localhost:3000 to see the platform.

Verifying the setup

./scripts/start-mobile-env.sh status

Expected output:

  • Android emulator: running
  • Appium server: listening on 4723
  • mobile-test-agent: process running, polling for jobs
  • Backend services: all healthy

Configuring a mobile app for T1

In the UI Configuration → Mobile Apps:

FieldValue
Nameanything readable, e.g. "SauceLabs Demo App"
PlatformANDROID
App packagecom.swaglabsmobileapp (or your app's package)
Cloud providerNONE ← the key bit

Upload your APK using Upload Binary. The agent will pull it from MinIO at job time.

Writing your first mobile test

Same YAML shape as UI tests, different action set:

steps:
- action: tap
selector: accessibility:test-Username
- action: type
selector: accessibility:test-Username
value: standard_user
- action: tap
selector: accessibility:test-Password
- action: type
selector: accessibility:test-Password
value: secret_sauce
- action: tap
selector: accessibility:test-LOGIN
- action: assert_visible
selector: accessibility:test-PRODUCTS

See yaml-format for the full action reference.

Troubleshooting

Emulator doesn't start

  • Check adb devices — should show one emulator-5554 line. If empty, the emulator's still booting (takes 30-60s on first run) or the AVD is corrupt.
  • Recreate the AVD: ./scripts/install-android-sdk.sh recreate-avd

Appium says "no device"

  • Restart Appium: pkill -f appium && appium &
  • Verify Appium can see the device: appium driver list should show uiautomator2

Agent isn't picking up jobs

  • Check the JWT token isn't expired — ./scripts/start-mobile-env.sh status checks this
  • Tail the agent log: tail -f /tmp/mobile-test-agent.log

"Docker daemon not running"

  • Docker Desktop must be open. open -a Docker, wait ~30s.

Switching to a different tier

The same mobile app + test can run on T2 (self-hosted) or T4 (vendor) by changing the cloud_provider field:

The YAML doesn't change. Same selectors, same assertions.

What's next

  • Device farms — graduate to BrowserStack / Sauce Labs for iOS or real devices
  • YAML format — full mobile action reference
  • Credentials — store login credentials so tests aren't littered with secrets