Lesson 7 of 9 · Advanced Quality Engineering
Mobile Testing
Test native, hybrid and mobile web apps: emulators vs real devices, Appium 2+ architecture and drivers, a first Appium test, Playwright emulation limits and mobile-specific risks.
intermediate 8 min read Updated
You will learn to
- Distinguish native, hybrid and mobile web apps and what each means for test tooling
- Choose between emulators, simulators and real devices for a given risk
- Explain the Appium 2+ server and driver architecture and write a first Appium test
- Describe what Playwright device emulation does and does not cover
Prerequisites
- Selenium or Playwright basics
- Test Case Design
- Cloud Testing Platforms
Mobile testing is not web testing on a smaller screen. Phones lose network in lifts, apps are paused and killed by the operating system, permissions are granted and revoked, screens rotate, keyboards cover inputs, and users run several OS versions on hardware with very different performance. Automation tooling is also different: the browser automation you know covers only part of the mobile picture.
Three kinds of mobile app
| Type | What it is | How you automate it |
|---|---|---|
| Native | Built with platform SDKs (Swift/Objective-C for iOS, Kotlin/Java for Android) or cross-platform frameworks that render native views | Platform frameworks (XCUITest, Espresso, UI Automator) or Appium on top of them |
| Hybrid | A native shell with some or all screens rendered in an embedded web view | Appium, switching between the native context and the web view context |
| Mobile web | A website in a mobile browser such as Safari on iOS or Chrome on Android | Appium driving the mobile browser, a cloud device, or emulation in desktop browser tools for layout checks |
Frameworks like React Native and Flutter complicate the picture: React Native renders native components, so it is automated like a native app; Flutter draws its own widgets, and the accessibility identifiers it exposes determine how well generic tools can find elements. Ask the developers how the app is built before choosing tools.
Emulators, simulators and real devices
- Android emulators (from Android Studio) emulate a full Android device, including the OS. They are free, scriptable and good for functional testing across OS versions.
- iOS simulators (from Xcode, macOS only) simulate iOS on the Mac’s own architecture. They are fast but are not the real iOS on real hardware; some hardware features are missing or behave differently.
- Real devices are the only way to see real performance, battery, camera, biometrics, push notification delivery, carrier networks, manufacturer customisations and true touch behaviour.
A balanced strategy:
- Run most automated functional tests on emulators and simulators in CI: they are cheap, parallel and reproducible.
- Run a smaller set of critical journeys on real devices, either in a device lab or a cloud device farm, nightly or before release.
- Do exploratory testing on real devices, especially for gestures, performance feel, interruptions and accessibility with screen readers.
Pick the device and OS matrix from your own analytics. A pairwise generator helps reduce combinations of OS version, screen size and manufacturer to a manageable set.
Appium architecture
Appium is an open-source automation framework for mobile (and some desktop) apps that speaks the W3C WebDriver protocol, so the client side looks familiar to Selenium users.
Since Appium 2, the architecture is modular:
- The Appium server is a Node.js program that receives WebDriver commands.
- Drivers are installed separately and do the platform-specific work. The common ones are UiAutomator2 for Android (built on Google’s UI Automator) and XCUITest for iOS (built on Apple’s XCTest framework). Others exist for Espresso, Flutter, Windows and macOS apps.
- Plugins can extend the server’s behaviour.
- Client libraries exist for Java, Python, JavaScript (WebdriverIO is a popular choice), C# and Ruby.
Setup looks like this:
npm install -g appium
appium driver install uiautomator2
appium driver install xcuitest # macOS with Xcode only
appium driver list --installed
appium # starts the server on http://127.0.0.1:4723
Android also needs the Android SDK with ANDROID_HOME set and a running emulator or connected device; iOS needs Xcode. The appium-doctor utility has historically helped check prerequisites; check the current Appium documentation for the recommended way, since tooling around Appium has evolved between major versions.
A notable change from Appium 1: the default server base path is /, not /wd/hub. Old examples that connect to http://localhost:4723/wd/hub fail against a default Appium 2+ server.
Capabilities
A session starts with capabilities describing what to automate. Standard W3C capabilities like platformName are unprefixed; Appium-specific ones use the appium: prefix:
{
"platformName": "Android",
"appium:automationName": "UiAutomator2",
"appium:deviceName": "Android Emulator",
"appium:app": "/builds/shop-app-2.14.0.apk",
"appium:newCommandTimeout": 120
}
A first Appium test in Java
The Java client provides typed options classes that set these capabilities for you:
import io.appium.java_client.AppiumBy;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.options.UiAutomator2Options;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.net.URI;
import java.time.Duration;
import static org.junit.jupiter.api.Assertions.assertEquals;
class LoginTest {
private AndroidDriver driver;
@BeforeEach
void setUp() throws Exception {
UiAutomator2Options options = new UiAutomator2Options()
.setDeviceName("Android Emulator")
.setApp(System.getenv("APP_PATH"));
driver = new AndroidDriver(URI.create("http://127.0.0.1:4723").toURL(), options);
}
@Test
void validUserSeesHomeScreen() {
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(15));
driver.findElement(AppiumBy.accessibilityId("email-input")).sendKeys("qa.user@example.com");
driver.findElement(AppiumBy.accessibilityId("password-input")).sendKeys(System.getenv("TEST_PASSWORD"));
driver.findElement(AppiumBy.accessibilityId("login-button")).click();
WebElement greeting = wait.until(
ExpectedConditions.visibilityOfElementLocated(AppiumBy.accessibilityId("home-greeting")));
assertEquals("Welcome back", greeting.getText());
}
@AfterEach
void tearDown() {
if (driver != null) {
driver.quit();
}
}
}
Locator strategy
- Accessibility ID is the preferred locator: it maps to
content-descon Android andaccessibilityIdentifieron iOS. Ask developers to add stable identifiers; it also improves accessibility when done properly. - Resource ID (Android
id) is also stable. - XPath works but is slow on mobile, because the driver must serialise the whole UI hierarchy, and brittle across OS versions. Use it as a last resort.
- Platform-specific selectors such as Android UI Automator expressions or iOS predicate strings and class chains are fast and powerful but tie tests to one platform.
Appium Inspector (a separate desktop or web app) shows the element tree and attributes of a running session, which is the mobile equivalent of browser DevTools.
Hybrid apps
In a hybrid app, the driver starts in the NATIVE_APP context. To interact with web view content using CSS selectors, list the available contexts and switch to the WEBVIEW_... one, then switch back for native screens. The web view must be debuggable in the build you test, which is normally enabled only for test or debug builds.
Playwright and mobile
Playwright can emulate mobile devices in its desktop browsers:
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
projects: [
{ name: 'mobile-chrome', use: { ...devices['Pixel 7'] } },
{ name: 'mobile-safari', use: { ...devices['iPhone 15'] } },
],
});
A device descriptor sets the viewport, device scale factor, user agent, touch support and isMobile, and selects the browser engine (Chromium for Android devices, WebKit for iOS devices). This is excellent for testing responsive layouts, touch-specific UI and mobile navigation quickly on every PR.
Mobile-specific test ideas
Whatever the tool, these risks are where mobile bugs hide:
- Interruptions: incoming call, notification, switching apps mid-payment, locking the screen during a form.
- Lifecycle: the OS kills the app in the background; does it restore state on return?
- Network: offline, flaky, slow and switching between Wi-Fi and mobile data; requests retried without duplicating orders.
- Permissions: denied, granted later, revoked in settings while the app is running.
- Orientation and screen sizes: rotation mid-flow, small screens, large fonts, split screen.
- Keyboard: the on-screen keyboard covering inputs or buttons; input types (numeric, email) showing the right keyboard.
- Upgrades: installing the new version over the old one with existing data, and supporting older API versions until users update.
- Accessibility: VoiceOver and TalkBack labels, focus order and touch target sizes.
- Device resources: low storage, low battery mode, older hardware.
Many of these are best covered by exploratory testing on real devices with a charter, supported by automation for the regression paths.
Practice exercise
Your company has a React Native shopping app and a responsive mobile website. Analytics show most users on recent iOS and Android versions, with a meaningful share on Android devices that are a few years old. Propose a mobile test approach covering tools, devices and what runs when.
Show a model answer
Mobile website
- Playwright projects with
devices['Pixel 7']anddevices['iPhone 15']on every PR for layout, navigation and touch-specific behaviour. - A small set of critical journeys on real Safari (iOS) and Chrome (Android) through a device cloud, nightly and pre-release.
React Native app
- Ask developers to add
accessibilityLabelortestIDvalues (exposed as accessibility IDs) to key elements. - Appium 2+ with UiAutomator2 and XCUITest drivers, one shared test codebase, page objects per screen, accessibility ID locators and no XPath where avoidable.
- PR builds: smoke journeys on an Android emulator and an iOS simulator in CI.
- Nightly: full regression on emulators and simulators across the main OS versions.
- Pre-release: critical journeys on real devices, including an older, lower-powered Android device from the analytics.
Manual and exploratory
- Charters for interruptions, offline and flaky networks, permissions, upgrade from the previous version with existing data, and screen reader use, on real devices.
Device matrix
- Driven by analytics, reviewed quarterly, reduced with pairwise combinations of OS version, screen size and manufacturer.
Key takeaways
- Identify whether the app is native, hybrid or mobile web; it decides the tooling.
- Use emulators and simulators for broad, cheap automation and real devices for performance, hardware and real-world behaviour.
- Appium 2+ separates the server from platform drivers (UiAutomator2, XCUITest); the default base path is
/. - Prefer accessibility ID locators; avoid XPath on mobile where you can.
- Playwright device emulation is valuable for responsive web testing but is not a real device and cannot test native apps.
- Interruptions, lifecycle, network, permissions and upgrades are where mobile-specific bugs live.
Key terms
Finished this lesson?
Progress is saved in this browser. No account needed.