Mobile apps are everywhere, but "mobile app" is not one thing. It is three distinct architectural patterns — native, hybrid, and mobile web — and each one demands a different testing mindset, a different set of tools, and a different failure mode to watch for. If you blur these categories, you will spend a lot of time wondering why your locator strategy works on one screen but breaks on another. This lesson draws the lines clearly.
Native apps
A native app is built using the platform's own SDK — Swift or Objective-C for iOS, Kotlin or Java for Android. It is compiled into a binary that runs directly on the device's operating system. The UI is rendered entirely by the platform's own widget toolkit.
From a testing perspective, native apps expose their elements through a platform-specific accessibility tree:
- Android — UIAutomator2 reads an XML hierarchy of
android.widget.Button,android.widget.EditText, and other platform widget classes. Every element has attributes likeresource-id,content-desc,text, andclickable. - iOS — XCUITest reads an accessibility tree of
XCUIElementTypeButton,XCUIElementTypeTextField, and so on. Every element has anaccessibilityIdentifierandaccessibilityLabel.
Native apps give you the best performance and access to all platform features (camera, NFC, biometrics, push notifications), but they require platform-specific locators and two separate codebases to ship.
Hybrid apps
A hybrid app wraps one or more WebView components inside a native shell. The shell might be written with Cordova, Capacitor, or React Native's web renderer. The outer chrome (navigation bar, splash screen, device permissions) is native; the inner content is an HTML/CSS/JavaScript document rendered in a browser engine embedded in the app.
This split has a direct consequence for automation: your driver starts in NATIVE_APP context and can interact with the shell. When you need to test the web content inside the WebView, you must switch context:
// List available contexts
Set<String> contexts = driver.getContextHandles();
// Switch to the web context
driver.context("WEBVIEW_com.myapp.package");
// Now use standard CSS / XPath / ID selectors
driver.findElement(By.cssSelector("input[name='email']"));
// Switch back to native
driver.context("NATIVE_APP");Hybrid apps are popular with teams that want to share a React or Angular codebase across web and mobile. The testing complexity is the price: you have to manage context switches carefully, and WebView debugging requires Chrome DevTools Protocol to be enabled on Android.
Mobile web
Mobile web is your full web application running in a mobile browser — Chrome on Android, Safari on iOS. There is no app to install, no native shell, no context switching. The user opens a URL and the browser renders the page.
From an Appium perspective, mobile web testing is the closest to Selenium. You configure a browser session instead of an app session:
UiAutomator2Options options = new UiAutomator2Options();
options.setPlatformName("Android");
options.setDeviceName("emulator-5554");
options.withBrowserName("Chrome");
AndroidDriver driver = new AndroidDriver(new URL("http://127.0.0.1:4723"), options);
driver.get("https://yoursite.com");Mobile web tests fail for a different set of reasons than native tests: viewport-related layout bugs, responsive breakpoints that render elements off-screen, touch event handling, and browser-specific rendering differences are the main culprits.
Choosing the right test approach
Most real-world projects involve all three. A banking app might have:
- A fully native home screen and biometric login (native context)
- A Capacitor-wrapped web view for the account statement page (hybrid)
- A mobile browser flow for password reset sent via email link (mobile web)
Map each screen to its context before you write a single locator. A five-minute audit of the accessibility tree in Appium Inspector saves hours of debugging later.
What Appium does across all three
Appium handles all three app types because it implements the W3C WebDriver protocol and delegates to the appropriate underlying automation engine:
| App type | Android engine | iOS engine |
|---|---|---|
| Native | UIAutomator2 | XCUITest |
| Hybrid (native layer) | UIAutomator2 | XCUITest |
| Hybrid (web layer) | Chromedriver | SafariDriver |
| Mobile web | Chromedriver | SafariDriver |
This unified protocol means the same Java test class, the same TestNG runner, and the same CI pipeline can cover all three app types — the driver implementation changes under the hood.