Topic 1 of 120%
📱 Mobile Automation

Appium
Mobile Automation Testing Tutorial

Master mobile test automation with Appium — from architecture and setup to writing real tests for Android and iOS, locators, gestures, waits, and professional best practices.

⏱️ ~3.5 hrs 🎯 12 Topics 🤖 Android & iOS ☕ Java examples 🧪 Quiz each section
01
Introduction
What is Appium?
Appium is a free, open-source, cross-platform mobile test automation framework. It lets testers write automated tests for Android and iOS applications using the W3C WebDriver protocol — the same standard used by Selenium for web browsers. Tests can be written in Java, Python, JavaScript, Ruby, C#, and more.
🧠 Simple analogy: Think of Appium as a "universal remote control" for mobile apps. Just like a universal remote can control any TV brand using a standard set of buttons, Appium can control any Android or iOS app using a standard set of test commands — without you needing to know the internal mechanics of each platform.

Key facts about Appium:

Created by
Dan Cuellar in 2012 (originally "iOS Auto" in 2011). Now maintained by the open-source community.
License
Open source — Apache 2.0 License. Free to use.
Languages
Java, Python, JavaScript, Ruby, C#, PHP — write tests in any of these.
Platforms
Android, iOS, Windows (desktop), macOS, TV apps (via separate drivers)
Protocol
W3C WebDriver (same standard as Selenium) — making it familiar to Selenium users.
Current version
Appium 2.x — drivers are decoupled and installed separately (major change from v1).
Awards
InfoWorld Bossie Award 2014 (Best Open Source Desktop & Mobile Software). Black Duck Open Source Rookie of the Year.

What makes Appium unique and popular:

  • 1
    No app modification needed You do NOT need to add any special testing code inside your Android or iOS app. Appium tests the app as-is — just like a real user would interact with it. No recompilation, no special "test builds" required.
  • 2
    Cross-platform with one API Write tests using the same Appium API whether you target Android or iOS. Switching platforms is mostly a matter of changing Desired Capabilities — the test commands stay the same.
  • 3
    Multi-language support Your team can write Appium tests in Java, Python, JavaScript, Ruby, or C#. You are not locked into one language.
  • 4
    Supports all app types Native apps (built in Swift/Kotlin/Java), Hybrid apps (web view inside native shell), and Mobile Web apps (browser testing on mobile) — Appium handles all three.
  • 5
    Works on real devices AND emulators/simulators Run tests on physical Android/iOS devices connected via USB, or on Android Virtual Devices (AVD) and iOS Simulators — both fully supported.
ℹ️
Appium 2.0 — key change from version 1: In Appium 1.x, all drivers (Android, iOS, etc.) were bundled together in one big installation. In Appium 2.x, drivers are completely separate packages — you install only the ones you need. For Android testing you install the UiAutomator2 driver separately; for iOS you install the XCUITest driver separately. This makes Appium leaner and easier to update.
🧪 Quiz: What is the main advantage of Appium NOT requiring app modification for testing?
02
How It Works
Appium Architecture Deep Dive
Appium follows a Client-Server architecture. The test script (client) sends commands over HTTP to the Appium Server. The server translates these commands into platform-specific automation calls and forwards them to the device via platform drivers (UiAutomator2 for Android, XCUITest for iOS).

The 4 layers of Appium architecture:

🖥️ Appium Client
Your test script (Java, Python, etc.) — sends HTTP commands
↓ HTTP / W3C WebDriver Protocol ↓
⚙️ Appium Server
Node.js HTTP server — receives commands & routes to correct driver
↓ Platform-specific driver ↓
🔧 Platform Driver
UiAutomator2 (Android) | XCUITest (iOS) — translates commands
↓ ADB / WebDriverAgent ↓
📱 Device / Emulator
Real Android/iOS device or AVD/Simulator — executes actions

Step-by-step communication flow:

  • 1
    Test script sends a command Your Java/Python test calls a method like driver.findElement(By.id("login-btn")).click(). The Appium Client Library converts this into a JSON-formatted HTTP POST request.
  • 2
    Appium Server receives the request The Appium Server (running at http://127.0.0.1:4723 by default) receives the HTTP request. It reads the session capabilities to decide which driver to use.
  • 3
    Driver routes to the device For Android, the UiAutomator2 driver sends commands via ADB (Android Debug Bridge) to the device. For iOS, XCUITest uses WebDriverAgent (a WDA server running on the iOS device).
  • 4
    Device executes the action The device performs the action (clicks the button, types text, etc.) and returns the result back up the chain to your test script.
Android Driver
UiAutomator2 — Google's official UIAutomator2 framework. Supports Android 5.0 (API 21) and above. Uses ADB to communicate with device.
iOS Driver
XCUITest — Apple's official XCUITest framework (part of Xcode). Only runs on macOS. Uses WebDriverAgent running on the device.
ADB
Android Debug Bridge — command-line tool that connects to Android devices/emulators over USB or Wi-Fi. Essential component for Android automation.
Session
All automation happens inside a session. A session is created when your test starts (with capabilities), and destroyed when the test ends. Session ID is used for all subsequent commands.
Default server port
http://127.0.0.1:4723 — the Appium server listens here by default. Can be changed with --port flag.
ℹ️
Why does iOS automation require macOS? The XCUITest driver uses Xcode's native testing framework which is only available on macOS. You cannot run iOS automation tests from Windows or Linux. Android automation, however, works on Windows, macOS, and Linux since ADB is cross-platform.
🧪 Quiz: In Appium's architecture, what is the role of the Appium Server?
03
Fundamentals
App Types: Native, Hybrid & Mobile Web
Before writing Appium tests, you must know what type of app you are testing. The app type affects how Appium interacts with the UI, which locators work, and how you set up the session. Appium supports all three app types.
📱 Native App
Built with:Kotlin/Java (Android), Swift/Objective-C (iOS)
Examples:Gmail, WhatsApp, Instagram, Google Maps
Locators:ID, AccessibilityId, XPath, UIAutomator
Context:NATIVE_APP context only
Performance:Fastest — direct platform access
🔀 Hybrid App
Built with:HTML/CSS/JS inside a native container (WebView)
Examples:Apps built with Cordova, Ionic, React Native
Locators:CSS/XPath inside WebView, native locators outside
Context:Must switch between NATIVE_APP and WEBVIEW
Challenge:Context switching required for web content
🌐 Mobile Web App
Built with:Regular website opened in mobile browser
Examples:m.flipkart.com, m.amazon.in in Chrome
Locators:CSS selectors, XPath (same as Selenium)
Setup:Set browserName capability (Chrome/Safari)
Note:No APK/IPA needed — just a URL
Feature📱 Native App🔀 Hybrid App🌐 Mobile Web
TechnologyKotlin/Java or SwiftHTML/CSS/JS in WebViewWebsite in browser
ExamplesGmail, WhatsAppIonic, React Native appsm.amazon.in in Chrome
LocatorsID, AccessibilityIdNative + CSS/XPathCSS, XPath
Context switchNot neededMust switch NATIVE↔WEBVIEWNot needed
Appium capabilityapp: /path/to.apkapp: /path/to.apkbrowserName: Chrome
PerformanceFastestMediumDepends on browser
🔀 Context Switching in Hybrid Apps
Hybrid apps have TWO areas: the native shell (buttons, navigation bar) and the WebView (the HTML content inside). To test the web content, you must switch context from NATIVE to WEBVIEW. To test native elements again, switch back.
Context Switching — Hybrid App (Java)
// Get all available contexts in the app
Set<String> contexts = driver.getContextHandles();
// Prints: [NATIVE_APP, WEBVIEW_com.example.app]

// Switch TO WebView to interact with web content
driver.context("WEBVIEW_com.example.app");
driver.findElement(By.cssSelector("#login-btn")).click();

// Switch BACK to native context
driver.context("NATIVE_APP");
driver.findElement(By.id("com.example:id/back_btn")).click();
🧪 Quiz: You are testing an app built with Ionic framework. It has a native navigation bar and HTML content inside. What type of app is this and what special step does Appium require?
04
Setup
Installation & Environment Setup
Appium setup requires multiple prerequisites. For Android automation, you need Java JDK, Android Studio (for SDK and emulators), Node.js, and Appium. For iOS automation (macOS only), you additionally need Xcode and install the XCUITest driver.

Step-by-step setup for Android automation (most common):

  • 1
    Install Java JDK 17+ Download from adoptium.net. After installing, set environment variable: JAVA_HOME = C:\Program Files\Java\jdk-17 and add %JAVA_HOME%\bin to PATH. Verify: java -version
  • 2
    Install Android Studio Download from developer.android.com. This installs the Android SDK, ADB, and AVD Manager. Set environment variable: ANDROID_HOME = C:\Users\YourName\AppData\Local\Android\Sdk and add %ANDROID_HOME%\platform-tools to PATH.
  • 3
    Install Node.js 18+ Download from nodejs.org. Appium Server is a Node.js application and requires npm to install. Verify: node --version and npm --version
  • 4
    Install Appium globally Run: npm install -g appium — this installs the Appium server only (no drivers in Appium 2.x). Verify: appium --version
  • 5
    Install UiAutomator2 driver (for Android) Run: appium driver install uiautomator2. This installs the Android automation driver separately. This is required in Appium 2.x.
  • 6
    Run Appium Doctor to check setup Install: npm install -g appium-doctor, then run: appium-doctor --android. This checks all dependencies and tells you what is missing or misconfigured.
  • 7
    Start the Appium Server Run: appium in terminal. You should see "Welcome to Appium v2.x" and "Appium REST http interface listener started on 0.0.0.0:4723". Keep this running while tests execute.
Essential installation commands
# Step 1: Install Appium server globally
npm install -g appium

# Step 2: Install Android driver (Appium 2.x — required!)
appium driver install uiautomator2

# Step 3: Install iOS driver (macOS only)
appium driver install xcuitest

# Step 4: Check all drivers installed
appium driver list

# Step 5: Install and run Appium Doctor (check dependencies)
npm install -g appium-doctor
appium-doctor --android   # Check Android setup
appium-doctor --ios       # Check iOS setup (macOS only)

# Step 6: Start Appium Server
appium

# Step 7: Verify ADB can see your device/emulator
adb devices
⚠️
Important — Appium 2.x change: Running npm install -g appium in Appium 2.x installs the server ONLY — no drivers are included. You must separately run appium driver install uiautomator2 for Android and appium driver install xcuitest for iOS. Without this, you'll get "No driver found" errors when running tests.
💡
Android Emulator setup: Open Android Studio → AVD Manager → Create Virtual Device. Choose a device (e.g., Pixel 8) and a system image (e.g., Android 14, API 34). Start the emulator. Then run adb devices to confirm Appium can see it. The emulator name (e.g., emulator-5554) is what you'll use in your Desired Capabilities.
🧪 Quiz: After installing Appium 2.x with npm install -g appium, a tester tries to run an Android test but gets "No driver found for Android". What did they forget?
05
Core Concept
Desired Capabilities
Desired Capabilities (or "Caps") are a set of key-value pairs that you send to the Appium Server when starting a test session. They tell Appium: which platform (Android/iOS), which device, which app to install and launch, which automation driver to use, and other session settings. Without capabilities, Appium cannot create a session.
🧠 Simple analogy: Desired Capabilities are like filling out a form before a flight — you specify which city (platform), which flight number (device), which seat class (driver), and special requirements. The airline (Appium Server) uses this information to prepare your exact session.

Essential capabilities for Android (UiAutomator2):

platformName
"Android" — Tells Appium which OS. Use "iOS" for iPhone/iPad testing.
automationName
"UiAutomator2" — Which driver to use for Android. Use "XCUITest" for iOS. This is the most important capability in Appium 2.x.
deviceName
Name of the device or emulator. e.g., "emulator-5554" or "Pixel 8". For real devices, use the device name from adb devices.
platformVersion
OS version. e.g., "14" for Android 14, "17.0" for iOS 17. Helps Appium select the right device if multiple are connected.
app
Full path to the APK file (Android) or IPA file (iOS). e.g., "/Users/priya/apps/Calculator.apk". Appium installs and launches this app.
appPackage
Android app's package name. e.g., "com.android.calculator2". Found in AndroidManifest.xml or via adb shell dumpsys window.
appActivity
The main activity (screen) to launch. e.g., "com.android.calculator2.Calculator". Tells Appium which screen to open first.
noReset
true = don't uninstall/reinstall app between sessions (faster). false = fresh install every time (cleaner state). Default is false.
Desired Capabilities — Android (Java)
import io.appium.java_client.android.options.UiAutomator2Options;
import io.appium.java_client.android.AndroidDriver;
import java.net.URL;

// Create UiAutomator2Options (recommended for Appium 2.x)
UiAutomator2Options options = new UiAutomator2Options();

options.setPlatformName("Android");
options.setPlatformVersion("14");
options.setDeviceName("emulator-5554");
options.setAutomationName("UiAutomator2");
options.setApp("/path/to/MyApp.apk");          // install & open this APK
options.setAppPackage("com.mycompany.myapp");
options.setAppActivity("com.mycompany.myapp.MainActivity");
options.setNoReset(true);                     // don't reinstall between sessions

// Create driver — connects to Appium Server
AndroidDriver driver = new AndroidDriver(
    new URL("http://127.0.0.1:4723"),  // Appium server URL
    options
);
Desired Capabilities — iOS (Java)
import io.appium.java_client.ios.options.XCUITestOptions;
import io.appium.java_client.ios.IOSDriver;

XCUITestOptions options = new XCUITestOptions();

options.setPlatformName("iOS");
options.setPlatformVersion("17.0");
options.setDeviceName("iPhone 15");
options.setAutomationName("XCUITest");
options.setApp("/path/to/MyApp.app");            // .app for simulator, .ipa for real device
options.setBundleId("com.mycompany.myapp");        // iOS app bundle identifier
options.setNoReset(true);

IOSDriver driver = new IOSDriver(
    new URL("http://127.0.0.1:4723"),
    options
);
💡
How to find appPackage and appActivity for any Android app:
1. Connect device/emulator and open the app manually
2. Run: adb shell dumpsys window | grep -E 'mCurrentFocus|mFocusedApp'
3. Output shows: com.example.app/com.example.app.MainActivity — the part before / is appPackage, after / is appActivity
🧪 Quiz: Which Desired Capability tells Appium which automation framework/driver to use for Android testing?
06
Essential Tool
Appium Inspector
Appium Inspector is a GUI desktop application used to inspect the UI of a mobile app running on a device or emulator. It shows you the full XML hierarchy of the app's screen and lets you identify element attributes (ID, AccessibilityId, XPath, class name) needed to write locators in your test scripts. Think of it as DevTools for mobile apps.

How to use Appium Inspector:

  • 1
    Download Appium Inspector From github.com/appium/appium-inspector — available for Windows, macOS, and Linux. Install like any desktop application.
  • 2
    Start Appium Server first Run appium in terminal. Appium Inspector connects to this running server. Server must be running before you open Inspector.
  • 3
    Fill in Remote Host settings In Inspector, set: Remote Host = 127.0.0.1, Remote Port = 4723, Remote Path = leave empty (Appium 2.x default).
  • 4
    Enter Desired Capabilities as JSON In the "Desired Capabilities" section, paste your capabilities JSON — platform, device, app path etc. — then click "Start Session".
  • 5
    Inspect elements Inspector shows a screenshot of the app on the left. Click any element → Inspector shows all its attributes on the right panel (accessibility id, resource-id, class, text, XPath). Copy the locator to use in your tests.
📋 Sample capabilities JSON for Inspector (Android)
Paste this in the Appium Inspector capabilities section to start a session:
Inspector Capabilities JSON — Android
{
  "platformName": "Android",
  "appium:automationName": "UiAutomator2",
  "appium:deviceName": "emulator-5554",
  "appium:platformVersion": "14",
  "appium:app": "/path/to/MyApp.apk",
  "appium:noReset": true
}
Screenshot panel
Left side — live screenshot of the app. Click any visible element to highlight it and see its properties.
Source tree panel
Middle — full XML source of the current screen. Browse the element hierarchy. Click any node to see its attributes.
Selected Element panel
Right side — shows all attributes of the selected element: resource-id, accessibility id, class, text, XPath, bounds.
Suggested locators
Inspector suggests the best locators for the selected element — it ranks them from most stable (accessibility id, id) to least stable (xpath).
💡
Pro tip — use Inspector before writing every test: Always inspect the element first before writing a locator. Never guess — the actual resource-id may be different from what you expect. Inspector saves hours of debugging "Element not found" errors.
🧪 Quiz: What is the primary use of Appium Inspector in a test automation project?
07
Finding Elements
Locators — Finding Elements in Mobile Apps
In Appium, you find elements using driver.findElement(locatorStrategy). Unlike web Selenium which uses CSS and XPath, Appium has mobile-specific locator strategies designed for native app elements. Choosing the right locator makes tests fast and stable.

Locator strategy ranking — best to worst:

⭐ ID (resource-id) ⭐ Accessibility ID ✅ UIAutomator (Android) ✅ iOS Predicate String ⚠️ Class Name 🔴 XPath — last resort
All Locator Strategies with Examples (Java)
import org.openqa.selenium.By;
import io.appium.java_client.AppiumBy;

// ── 1. ID (resource-id) — ⭐ BEST for Android ─────────────────
// Android: uses resource-id like "com.package:id/elementId"
WebElement loginBtn = driver.findElement(
    By.id("com.myapp:id/login_button")
);

// ── 2. Accessibility ID — ⭐ BEST (cross-platform) ────────────
// Android: content-desc attribute | iOS: accessibility-id label
WebElement backBtn = driver.findElement(
    AppiumBy.accessibilityId("Go Back")
);

// ── 3. Android UIAutomator — ✅ GOOD Android-specific ─────────
// Use text(), resourceId(), className(), contentDesc()
WebElement item = driver.findElement(
    AppiumBy.androidUIAutomator("new UiSelector().text(\"Login\")")
);
// Find by resource ID using UIAutomator
WebElement pwd = driver.findElement(
    AppiumBy.androidUIAutomator(
        "new UiSelector().resourceId(\"com.myapp:id/password\")")
);

// ── 4. iOS Predicate String — ✅ GOOD iOS-specific ────────────
WebElement field = driver.findElement(
    AppiumBy.iOSNsPredicateString("type == 'XCUIElementTypeButton' AND label == 'Login'")
);

// ── 5. Class Name — ⚠️ USE CAREFULLY ─────────────────────────
// Android: "android.widget.Button", "android.widget.EditText"
WebElement btn = driver.findElement(
    By.className("android.widget.Button")   // finds FIRST button
);

// ── 6. XPath — 🔴 LAST RESORT (slow, fragile) ────────────────
WebElement el = driver.findElement(
    By.xpath("//android.widget.Button[@text='Login']")
);

// Find MULTIPLE elements (returns a list)
List<WebElement> allItems = driver.findElements(
    By.id("com.myapp:id/list_item")
);
System.out.println("Total items: " + allItems.size());
ID (resource-id)
🤖 Android Fastest and most reliable. Format: com.package.name:id/element_id. View in Appium Inspector as "resource-id". Use this whenever available.
Accessibility ID
🔄 Both Recommended for cross-platform apps. Android: "content-desc" attribute. iOS: "accessibility-id" label. Set by developers to improve app accessibility.
UIAutomator
🤖 Android only Uses Google's UIAutomator framework. Powerful text-based search. Also supports UiScrollable for scrolling to off-screen elements.
iOS Predicate
🍎 iOS only Uses iOS Predicate String for fast, stable element lookup. More readable and faster than XPath for iOS apps.
XPath
🔄 Both Most flexible but slowest — scans entire XML tree. Breaks easily when UI changes. Use only when no ID or accessibility id is available.
⚠️
Why XPath is discouraged in Appium: XPath in Appium scans the entire UI XML tree — this can take 1–5 seconds per element lookup on complex screens. It also breaks whenever the app's layout structure changes. Always prefer ID or AccessibilityId first, UIAutomator/Predicate second, and XPath only as a last resort.
🧪 Quiz: You want to write a locator that works on BOTH Android and iOS without any platform-specific code. Which locator strategy should you use?
08
Writing Tests
Your First Appium Test (Java + TestNG)
A complete Appium test uses Java + TestNG (or JUnit). You create a driver with capabilities in @BeforeMethod, write test steps in @Test, and quit the driver in @AfterMethod. The test connects to the running Appium server to control the device.

Maven pom.xml dependencies needed:

pom.xml — Required dependencies
<dependencies>
  <!-- Appium Java Client -->
  <dependency>
    <groupId>io.appium</groupId>
    <artifactId>java-client</artifactId>
    <version>9.3.0</version>
  </dependency>
  <!-- TestNG for test structure -->
  <dependency>
    <groupId>org.testng</groupId>
    <artifactId>testng</artifactId>
    <version>7.10.2</version>
  </dependency>
</dependencies>

Complete first test — Login flow on Android app:

LoginTest.java — Complete Appium Test
package tests;

import io.appium.java_client.AppiumBy;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.options.UiAutomator2Options;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.annotations.*;
import java.net.URL;
import java.time.Duration;

public class LoginTest {

    private AndroidDriver driver;

    @BeforeMethod
    public void setUp() throws Exception {
        // Define capabilities
        UiAutomator2Options options = new UiAutomator2Options();
        options.setPlatformName("Android");
        options.setPlatformVersion("14");
        options.setDeviceName("emulator-5554");
        options.setAutomationName("UiAutomator2");
        options.setApp(System.getProperty("user.dir") + "/apps/MyApp.apk");
        options.setAppPackage("com.mycompany.myapp");
        options.setAppActivity("com.mycompany.myapp.LoginActivity");
        options.setNoReset(false);

        // Connect to Appium Server
        driver = new AndroidDriver(
            new URL("http://127.0.0.1:4723"), options
        );
    }

    @Test
    public void testValidLogin() {
        // Wait for email field then type email
        WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
        WebElement emailField = wait.until(
            ExpectedConditions.visibilityOfElementLocated(
                By.id("com.mycompany.myapp:id/et_email")
            )
        );
        emailField.sendKeys("[email protected]");

        // Find and fill password field
        driver.findElement(
            By.id("com.mycompany.myapp:id/et_password")
        ).sendKeys("Test@123");

        // Click Login button
        driver.findElement(
            AppiumBy.accessibilityId("Login Button")
        ).click();

        // Verify dashboard screen appeared
        WebElement dashboardTitle = wait.until(
            ExpectedConditions.visibilityOfElementLocated(
                By.id("com.mycompany.myapp:id/tv_welcome")
            )
        );
        // Assert welcome message is displayed
        assert dashboardTitle.isDisplayed() : "Dashboard not loaded!";
        System.out.println("Login test PASSED. Welcome: " + dashboardTitle.getText());
    }

    @AfterMethod
    public void tearDown() {
        if (driver != null) {
            driver.quit();  // ends session, closes app
        }
    }
}
ℹ️
Key methods in every Appium test:
driver.findElement(locator) — finds a single element
element.sendKeys("text") — types text into a field (like type() in Cypress)
element.click() — clicks the element
element.getText() — gets the visible text of the element
element.isDisplayed() — returns true/false if element is visible
driver.quit() — ends session and closes the app (always call in teardown)
🧪 Quiz: In a TestNG-based Appium test, where should you create the AndroidDriver (connect to Appium Server) and why?
09
Interactions
Actions — Interacting With Mobile Elements
Once you find an element, you interact with it using action methods. These include clicking buttons, typing text, clearing fields, reading text, checking state, and navigating the app. Most methods come from the WebElement interface inherited from Selenium.
Common Appium Actions (Java)
// ── CLICK ────────────────────────────────────────────────────
driver.findElement(By.id("com.myapp:id/login_btn")).click();

// ── TYPE TEXT ────────────────────────────────────────────────
driver.findElement(By.id("com.myapp:id/et_email"))
      .sendKeys("[email protected]");

// ── CLEAR FIELD then TYPE ────────────────────────────────────
WebElement field = driver.findElement(By.id("com.myapp:id/et_email"));
field.clear();
field.sendKeys("[email protected]");

// ── READ TEXT from element ───────────────────────────────────
String welcomeMsg = driver
    .findElement(By.id("com.myapp:id/tv_welcome"))
    .getText();  // returns "Welcome, Priya!"

// ── CHECK if element is displayed ───────────────────────────
boolean isVisible = driver
    .findElement(By.id("com.myapp:id/error_msg"))
    .isDisplayed();

// ── CHECK if element is ENABLED (interactive) ───────────────
boolean isEnabled = driver
    .findElement(By.id("com.myapp:id/submit_btn"))
    .isEnabled();

// ── GET attribute value ─────────────────────────────────────
String hint = driver
    .findElement(By.id("com.myapp:id/et_email"))
    .getAttribute("hint");  // gets placeholder text

// ── NAVIGATE BACK (Android hardware back button) ─────────────
driver.navigate().back();

// ── HIDE KEYBOARD ───────────────────────────────────────────
driver.hideKeyboard();

// ── TAKE SCREENSHOT ─────────────────────────────────────────
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.apache.commons.io.FileUtils;
import java.io.File;

File screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
FileUtils.copyFile(screenshot, new File("screenshots/login_test.png"));
💡
driver.hideKeyboard() — when to use it: After typing in an input field on a real Android device, the on-screen keyboard often covers other elements (like the Login button). Call driver.hideKeyboard() after typing to dismiss the keyboard before clicking the next element. This is a very common requirement in mobile automation that has no equivalent in web Selenium.
🧪 Quiz: After a user types their email and password in a mobile app, the Login button is hidden behind the on-screen keyboard. What Appium method should you call before clicking the Login button?
10
Mobile Specific
Mobile Gestures — Swipe, Scroll, Long Press
Mobile apps rely on touch gestures — swipe, scroll, long press, pinch — that don't exist in web testing. Appium provides two ways to perform gestures: Mobile Commands via JavascriptExecutor (recommended for Appium 2.x) and the legacy TouchAction class.
⚠️
Important — TouchAction is deprecated in Appium 2.x: The old TouchAction class is deprecated in Appium 2.0 and above. The recommended approach is to use mobile gesture commands via JavascriptExecutor (e.g., driver.executeScript("mobile: swipeGesture", ...)) for Android, or W3C Actions API. Some projects still use TouchAction for compatibility — know both.
Mobile Gestures — Recommended (Appium 2.x Android)
import java.util.HashMap;
import java.util.Map;

// ── SWIPE GESTURE (using mobile: swipeGesture) ───────────────
// Swipe UP on the screen (scroll down content)
Map<String, Object> swipeArgs = new HashMap<>();
swipeArgs.put("left", 100);
swipeArgs.put("top", 700);
swipeArgs.put("width", 400);
swipeArgs.put("height", 500);
swipeArgs.put("direction", "up");   // up, down, left, right
swipeArgs.put("percent", 0.75);    // swipe 75% of the area
driver.executeScript("mobile: swipeGesture", swipeArgs);

// ── SCROLL TO ELEMENT (UIScrollable — Android only) ──────────
// Scrolls the list until element with text "Settings" is found
driver.findElement(
    AppiumBy.androidUIAutomator(
        "new UiScrollable(new UiSelector().scrollable(true))" +
        ".scrollIntoView(new UiSelector().text(\"Settings\"))"
    )
);

// ── LONG PRESS (using mobile: longClickGesture) ──────────────
WebElement element = driver.findElement(
    By.id("com.myapp:id/item_card")
);
Map<String, Object> longPressArgs = new HashMap<>();
longPressArgs.put("elementId",
    ((RemoteWebElement) element).getId());
longPressArgs.put("duration", 2000);    // hold for 2 seconds
driver.executeScript("mobile: longClickGesture", longPressArgs);

// ── TAP at specific coordinates ──────────────────────────────
Map<String, Object> tapArgs = new HashMap<>();
tapArgs.put("x", 540);
tapArgs.put("y", 1200);
driver.executeScript("mobile: clickGesture", tapArgs);

// ── LEGACY TouchAction (older style, still used in many projects) ──
import io.appium.java_client.TouchAction;
import io.appium.java_client.touch.WaitOptions;
import io.appium.java_client.touch.offset.PointOption;

new TouchAction(driver)
    .press(PointOption.point(540, 1500))   // start point
    .waitAction(WaitOptions.waitOptions(Duration.ofMillis(500)))
    .moveTo(PointOption.point(540, 600))   // end point (scroll up)
    .release()
    .perform();
Swipe UP
Moves finger from bottom to top — scrolls content DOWN (like scrolling down a page). Direction "up" means the finger moves up.
Swipe DOWN
Moves finger from top to bottom — scrolls content UP (pull-to-refresh gesture). Direction "down" means the finger moves down.
UIScrollable
🤖 Android only The easiest way to scroll to off-screen elements. Provide the text or ID of the element you want to reach — Appium handles the scrolling automatically.
Long Press
Press and hold an element for a duration (in milliseconds). Used to trigger context menus, drag-and-drop, or delete options in list items.
🧪 Quiz: You need to scroll down a long product list on Android until an element with text "Add to Cart" is visible. What is the most efficient Appium approach?
11
Reliability
Waits & Synchronization
Mobile apps are slower than websites — screens take time to load, animations play, API calls happen. If your test tries to find an element before it appears, it fails with "NoSuchElementException". Waits tell Appium to pause and keep retrying until the element is ready. Always use waits — never use Thread.sleep().
Implicit Wait
Set once for the entire driver session. When any findElement fails, Appium waits UP TO this time before throwing an error. Global, applies to all elements. Set once in setUp().
Explicit Wait
Waits for a specific condition on a specific element. More precise. Created with WebDriverWait + ExpectedConditions. Recommended for most cases.
Fluent Wait
Like Explicit Wait but you control the polling interval and can ignore specific exceptions. Most flexible but more verbose.
Thread.sleep()
🔴 NEVER use this. It's a fixed hardcoded pause — slow when things are fast, still fails when things are slow. It's not a real wait.
Wait strategies in Appium (Java)
// ── 1. IMPLICIT WAIT — set once for whole session ─────────────
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
// Now every findElement waits up to 10 seconds before failing

// ── 2. EXPLICIT WAIT — wait for specific condition ────────────
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(15));

// Wait until element is VISIBLE
WebElement el = wait.until(
    ExpectedConditions.visibilityOfElementLocated(
        By.id("com.myapp:id/dashboard_title")
    )
);

// Wait until element is CLICKABLE
WebElement btn = wait.until(
    ExpectedConditions.elementToBeClickable(
        AppiumBy.accessibilityId("Checkout Button")
    )
);
btn.click();

// Wait until text matches exactly
wait.until(
    ExpectedConditions.textToBePresentInElementLocated(
        By.id("com.myapp:id/status_msg"),
        "Order Confirmed!"
    )
);

// Wait until element is NOT visible (e.g. loading spinner disappears)
wait.until(
    ExpectedConditions.invisibilityOfElementLocated(
        By.id("com.myapp:id/loading_spinner")
    )
);

// ── 3. FLUENT WAIT — custom polling + ignore exceptions ───────
import org.openqa.selenium.support.ui.FluentWait;
import org.openqa.selenium.NoSuchElementException;

FluentWait<AndroidDriver> fluentWait = new FluentWait<>(driver)
    .withTimeout(Duration.ofSeconds(20))
    .pollingEvery(Duration.ofMillis(500))   // check every 500ms
    .ignoring(NoSuchElementException.class);

WebElement dynamicEl = fluentWait.until(d ->
    d.findElement(By.id("com.myapp:id/dynamic_content"))
);
🚫
Never use Thread.sleep() in production tests:
BAD: Thread.sleep(3000); — waits ALWAYS 3 seconds even if element loads in 0.5s
GOOD: wait.until(ExpectedConditions.visibilityOfElementLocated(...)) — waits ONLY until element is ready

Thread.sleep makes tests slow, flaky, and unprofessional. It will be called out in code reviews.
🧪 Quiz: A loading spinner appears on the app screen after clicking "Search". Your test must not proceed until the spinner disappears and results load. Which wait is best?
12
Professional Skills
Best Practices for Appium Automation

These are professional best practices followed by senior mobile automation engineers. Apply them to build a stable, maintainable, and fast mobile automation suite.

  1. 1
    Use Appium Inspector BEFORE writing any locator Never guess a locator. Always open Inspector, inspect the element, and use the suggested locator. This prevents "Element not found" errors and saves debugging time.
  2. 2
    Locator priority: ID → Accessibility ID → UIAutomator → XPath Always prefer ID first, then Accessibility ID. Ask developers to add accessibility IDs to important elements. Avoid XPath — it's slow and fragile in mobile apps.
  3. 3
    Never use Thread.sleep() — use Explicit Waits Thread.sleep is unpredictable and slows down tests. Use WebDriverWait + ExpectedConditions for all timing. This is the #1 sign of test quality.
  4. 4
    Always call driver.quit() in @AfterMethod Failing to quit the driver leaves sessions open on the Appium server, which causes "Max sessions reached" errors. Use try-finally in tearDown to guarantee it runs even if the test fails.
  5. 5
    Use Page Object Model (POM) pattern Create a separate class for each screen (LoginPage, HomePage, CartPage). Store locators and screen actions in these classes. Test methods just call page methods like loginPage.login("user", "pass"). This makes tests easier to maintain when the app UI changes.
  6. 6
    Test on real devices, not only emulators Emulators are good for quick development testing, but real devices reveal hardware-specific bugs — touch events, camera, GPS, network behavior. Always run final regression on real devices before release.
  7. 7
    Hide the keyboard after typing in input fields Always call driver.hideKeyboard() after filling input fields, especially on real Android devices. The keyboard covers buttons and causes click failures if not dismissed.
  8. 8
    Set noReset = true during development, false in CI During test development, set noReset: true to skip reinstalling the app every run — faster iteration. In CI/CD pipelines, set noReset: false for a clean, predictable test environment.
  9. 9
    Run appium-doctor before every fresh setup Run appium-doctor --android after any machine setup or dependency update. It checks all environment variables, SDK paths, ADB, Java version — preventing hours of confusing setup errors.
  10. 10
    Take screenshots on test failure Add a TestNG listener (using @AfterMethod(alwaysRun=true)) that captures a screenshot when a test fails. These screenshots are invaluable for debugging failures in CI where you cannot see the device screen.
🎯
Interview-ready answer — "Explain how you would set up Appium for Android testing and write your first test":

"First, I install prerequisites: Java JDK 17+, Android Studio for the Android SDK and AVD Manager, Node.js, then Appium with npm install -g appium. In Appium 2.x, I install the UiAutomator2 driver separately with appium driver install uiautomator2. I run appium-doctor --android to verify the setup. I open Appium Inspector to inspect element locators. In my Java test, I create UiAutomator2Options with platformName, deviceName, automationName, appPackage, appActivity, then create an AndroidDriver connecting to http://127.0.0.1:4723. I use WebDriverWait for element visibility, findElement with By.id or AppiumBy.accessibilityId, sendKeys for input, click for buttons, and always call driver.quit() in @AfterMethod to end the session cleanly."
🧪 Final Quiz: Your Appium test fails in CI with "Max sessions reached" error every time. What is the most likely cause?

Ready to Master Appium in Real Projects?

STAD Solution's QA Automation course covers Appium end-to-end — Android & iOS, real devices, CI/CD integration, Page Object Model, and 100% placement support.

Explore Courses at STAD Solution →