Drive this hub as mobile web on a device/emulator with the same selectors, and understand what changes for native apps.
data-test CSS selectors you already use. For native app practice you need an actual app package; see the note at the bottom.# Node 18+ required
npm install -g appium
appium driver install uiautomator2 # Android
appium driver install xcuitest # iOS (macOS only)
appium # starts server on http://127.0.0.1:4723
# Optional but very useful: Appium Inspector (GUI to explore elements)
# https://github.com/appium/appium-inspector/releases<!-- pom.xml -->
<dependency>
<groupId>io.appium</groupId>
<artifactId>java-client</artifactId>
<version>9.3.0</version> <!-- use the latest -->
</dependency>Start an Android emulator, serve the hub on your machine, then reach it from the emulator using the special host alias 10.0.2.2 (that's how the emulator sees your computer's localhost).
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 java.net.URL;
public class CartPilotMobileWeb {
public static void main(String[] args) throws Exception {
UiAutomator2Options options = new UiAutomator2Options()
.setDeviceName("Android Emulator")
.withBrowserName("Chrome"); // mobile-web session
AndroidDriver driver = new AndroidDriver(
new URL("http://127.0.0.1:4723"), options);
// 10.0.2.2 == host machine's localhost, seen from the emulator
driver.get("http://10.0.2.2:8000/swaglabs/index.html");
driver.findElement(By.cssSelector("[data-test='username']")).sendKeys("standard_user");
driver.findElement(By.cssSelector("[data-test='password']")).sendKeys("Pass@123");
driver.findElement(By.cssSelector("[data-test='login-button']")).click();
boolean ok = !driver.findElements(By.cssSelector("[data-test^='item-']")).isEmpty();
System.out.println("Products visible on mobile web: " + ok);
driver.quit();
}
}from appium import webdriver
from appium.options.android import UiAutomator2Options
from selenium.webdriver.common.by import By
opts = UiAutomator2Options()
opts.device_name = "Android Emulator"
opts.browser_name = "Chrome"
driver = webdriver.Remote("http://127.0.0.1:4723", options=opts)
driver.get("http://10.0.2.2:8000/swaglabs/index.html")
driver.find_element(By.CSS_SELECTOR, "[data-test='username']").send_keys("standard_user")
driver.find_element(By.CSS_SELECTOR, "[data-test='password']").send_keys("Pass@123")
driver.find_element(By.CSS_SELECTOR, "[data-test='login-button']").click()
print("items:", len(driver.find_elements(By.CSS_SELECTOR, "[data-test^='item-']")))
driver.quit()XCUITestOptions + IOSDriver, set browserName("Safari"), and reach the host with your machine's LAN IP (e.g. http://192.168.1.x:8000) since the simulator shares your network.On mobile you often scroll to an element or tap by coordinates. W3C actions work through the Java/Python clients; for native locators you'd use AppiumBy.
// Scroll a native Android list to text (native context only)
driver.findElement(AppiumBy.androidUIAutomator(
"new UiScrollable(new UiSelector().scrollable(true))"
+ ".scrollIntoView(new UiSelector().text(\"Leave\"))"));
// Accessibility id is the cross-platform native locator of choice
driver.findElement(AppiumBy.accessibilityId("menu-pim"));| Locators | Web: CSS/XPath on the DOM. Native: accessibility id, id/resource-id, class chain, UIAutomator/predicate. |
| Context | Hybrid apps switch between NATIVE_APP and WEBVIEW_* via driver.context(...). |
| Capabilities | Native needs app (path to .apk/.ipa) or appPackage/appActivity instead of browserName. |
| Waits | Same WebDriverWait patterns; add gesture/scroll before interacting with off-screen elements. |
app to its path. This hub covers the mobile-web half of Appium; the native half is identical Appium mechanics against an installed package.The zip ships a tiny buildable Android app (appium-native/) with known accessibility ids and resource-ids, plus matching Java & Python tests (ci/appium/). Build it once, point the app capability at the APK, and practise the native flow: login, add-to-cart counter, checkbox, spinner, scroll-to-element.
cd appium-native
./gradlew assembleDebug
# -> app/build/outputs/apk/debug/app-debug.apk
# then, with an emulator + `appium` running:
mvn test -Dapp=$(pwd)/app/build/outputs/apk/debug/app-debug.apk # Java
APK=$(pwd)/app/build/outputs/apk/debug/app-debug.apk pytest ci/appium/native_test.py # PythonFull steps: appium-native/README.md and ci/appium/README.md.