In this practice you’ll get to try Appium. Appium is an open-source mobile automation framework. It drives real devices and emulators/simulators to test:
- Native apps (built with Swift/Kotlin, etc.)
- Hybrid apps (webviews inside native shells)
- Mobile web (Chrome on Android, Safari on iOS)
It speaks the WebDriver protocol (same family as Selenium), so Selenium skills transfer well. Appium 2 uses drivers (e.g., UiAutomator2 for Android, XCUITest for iOS) behind the scenes.
When to use it
- You need real device behavior (gestures, keyboards, permissions, camera, notifications).
- You must cover Android + iOS with one test approach.
- Your app is native or hybrid, or you want mobile web in a real browser.
What Appium is best at
- End-to-end mobile UI flows on real hardware or emulators.
- Reusing WebDriver patterns (locators, waits, assertions) across platforms.
- Plugging into any language/test runner (JS + Mocha/Jest, Java + JUnit, etc.).
Not ideal for
- Ultra-fast unit checks (use unit/UI component tests instead).
- Heavy visual-diff testing without extra tooling.
- Flaky locators in dynamic UIs without adding test IDs and robust waits.
What we’ll practice in this section
- Wikipedia search (mobile)
- Type into the search field, press Enter, wait for the results page, and assert the title contains “Playwright”.
- Negative login (mobile)
- On
https://the-internet.herokuapp.com/login
, submit invalid creds, wait for the error banner, and assert message + URL.
Wikipedia search (mobile web)
What we’ll do
- Open Wikipedia’s Special:Search page with query
Playwright
. - (Best-effort) dismiss any consent/banner if it appears.
- Wait for results to load and the page to fully navigate.
- Assert the title contains “Playwright”.
- Optionally assert the URL matches a
/wiki/...
article.
Why this example
- Teaches navigation + search flows that often trigger page reloads.
- Practices explicit waits (
until.elementLocated
,until.elementIsVisible
) to avoid stale/hidden elements. - Shows how to add best-effort banner handling without flaking the test.
- Uses a real site with dynamic behavior—great for learning stability tactics (timeouts, resilient selectors, “type + ENTER” patterns).
Disclaimer for this practice test:
- It hits a live site. Live sites change and the internet flakes. That means rare test flakiness.
- Consent banners vary by region. We already coded tolerant selectors and a “best effort” dismiss; that keeps it robust.
Create tests/wiki_mobile.spec.js
with:
const { Builder, By, Key, until } = require('selenium-webdriver');
const { expect } = require('chai');
describe('Wikipedia search (Appium + Android Chrome)', function () {
this.timeout(120000);
let driver;
before(async () => {
driver = await new Builder()
.usingServer('http://127.0.0.1:4723')
.withCapabilities({
platformName: 'Android',
browserName: 'Chrome',
'appium:automationName': 'UiAutomator2',
'appium:deviceName': 'Android Emulator',
'appium:chromedriverAutodownload': true
})
.build();
});
after(async () => {
if (driver) await driver.quit();
});
it('searches for Playwright and opens the article', async () => {
await driver.get('https://en.m.wikipedia.org/wiki/Special:Search?search=Playwright&go=Go');
await maybeDismissConsent(driver);
await driver.wait(until.urlContains('/Playwright'), 15000);
await driver.wait(until.titleMatches(/Playwright/i), 15000);
const url = await driver.getCurrentUrl();
const title = await driver.getTitle();
expect(url).to.match(/\/Playwright/i);
expect(title).to.match(/Playwright/i);
});
});
async function maybeDismissConsent(driver) {
const sels = [
By.css('button[aria-label*="accept"]'),
By.css('button[title*="accept"]'),
By.css('button[aria-label*="agree"]'),
By.css('.wmm-cookie-banner__btn--accept'),
By.css('.wmf-consent-banner .accept')
];
for (const sel of sels) {
try {
const btn = await driver.wait(until.elementLocated(sel), 3000);
await driver.wait(until.elementIsVisible(btn), 3000);
await btn.click();
break;
} catch (_) {}
}
}
Run your test.
Negative login (mobile)
The the-internet.herokuapp.com/login is perfect for a reliable “negative login” demo: stable markup, no cookie banners, and it works well on mobile Chrome.
What we’ll do
- Open the login page.
- Type wrong credentials.
- Click Login.
- Wait for the error banner.
- Assert the banner text says the username is invalid and we’re still on
/login
.
Why this example
- Teaches form fill + click (bread-and-butter for UI tests).
- Shows explicit waits for feedback (
until.elementLocated
+until.elementIsVisible
). - Uses a stable demo site: https://the-internet.herokuapp.com
- Demonstrates clean assertions on both message and URL, so we verify UI + navigation outcome.
Create tests/login_negative_mobile.spec.js
and paste this:
const { Builder, By, Key, until } = require('selenium-webdriver');
const { expect } = require('chai');
describe('Negative login (Appium + Android Chrome)', function () {
this.timeout(120000);
let driver;
before(async () => {
driver = await new Builder()
.usingServer('http://127.0.0.1:4723')
.withCapabilities({
platformName: 'Android',
browserName: 'Chrome',
'appium:automationName': 'UiAutomator2',
'appium:deviceName': 'Android Emulator',
'appium:chromedriverAutodownload': true,
})
.build();
});
after(async () => { if (driver) await driver.quit(); });
it('shows an error banner for invalid credentials', async () => {
await driver.get('https://the-internet.herokuapp.com/login');
const user = await driver.wait(until.elementLocated(By.id('username')), 15000);
await driver.wait(until.elementIsVisible(user), 5000);
await user.clear();
await user.sendKeys('not_a_user');
const pass = await driver.findElement(By.id('password'));
await pass.clear();
await pass.sendKeys('definitely_wrong', Key.ENTER);
const flash = await driver.wait(until.elementLocated(By.id('flash')), 15000);
const txt = (await flash.getText()).trim();
expect(txt).to.match(/Your (username|password) is invalid!/i); // handles either message
const url = await driver.getCurrentUrl();
expect(url).to.match(/\/login\b/);
});
});
Run your test.
Congratulations! You successfully ran two tests with Appium. For more practical examples make sure to check out the Playwright/Cucumber and Selenium section just as well!
I know Appium wasn’t the easiest run, but practicing with this tutorial, you can countinue your journey with Appium with confidence.
If you have any remaining questions, feel free to reach out to us and to our tester community to discuss any issues or methods further.