Gestures & Touch Actions
Mobile apps rely on native gestures — swipe, scroll, pinch, long-press, double-tap, drag — that have no equivalent in web testing. Appium provides the W3C Actions API for simulating complex touch interactions. Mastering mobile gestures enables testing of carousels, pull-to-refresh, maps, and any gesture-dependent feature.
Mobile Gestures with Appium
from appium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.actions.action_builder import ActionBuilder
from selenium.webdriver.common.actions.pointer_input import PointerInput
from selenium.webdriver.common.actions import interaction
# ══════════════════════════════════════════════════════════════
# W3C ACTIONS API (recommended — works on both Android and iOS)
# ══════════════════════════════════════════════════════════════
def swipe(driver, start_x, start_y, end_x, end_y, duration_ms=500):
"""Swipe from start point to end point"""
touch = PointerInput(interaction.POINTER_TOUCH, "touch")
actions = ActionBuilder(driver, mouse=touch)
actions.pointer_action.move_to_location(start_x, start_y)
actions.pointer_action.pointer_down()
actions.key_action.pause(duration_ms / 1000)
actions.pointer_action.move_to_location(end_x, end_y)
actions.pointer_action.pointer_up()
actions.perform()
def long_press(driver, element, duration_ms=1000):
"""Long press on an element"""
touch = PointerInput(interaction.POINTER_TOUCH, "touch")
actions = ActionBuilder(driver, mouse=touch)
actions.pointer_action.click_and_hold(element)
actions.key_action.pause(duration_ms / 1000)
actions.pointer_action.release()
actions.perform()
def double_tap(driver, element):
"""Double tap on element"""
ActionChains(driver).double_click(element).perform()
# ══════════════════════════════════════════════════════════════
# PRACTICAL GESTURE TEST EXAMPLES
# ══════════════════════════════════════════════════════════════
def test_pull_to_refresh(android_driver):
driver = android_driver
# Scroll up (simulates pull down gesture on list)
size = driver.get_window_size()
start_y = int(size["height"] * 0.2) # 20% from top
end_y = int(size["height"] * 0.8) # 80% down
center_x = int(size["width"] * 0.5)
swipe(driver, center_x, start_y, center_x, end_y, duration_ms=800)
# Verify refresh indicator appeared and data reloaded
wait = WebDriverWait(driver, 10)
wait.until(EC.presence_of_element_located(
(AppiumBy.ACCESSIBILITY_ID, "refresh-complete-indicator")
))
assert "Updated" in driver.find_element(
AppiumBy.ID, "com.myapp:id/last_updated"
).text
def test_swipe_to_delete_item(android_driver):
driver = android_driver
# Get list item position
item = driver.find_element(AppiumBy.ACCESSIBILITY_ID, "list-item-0")
item_center_y = item.location["y"] + item.size["height"] // 2
# Swipe left on the item to reveal delete button
start_x = int(driver.get_window_size()["width"] * 0.8)
end_x = int(driver.get_window_size()["width"] * 0.1)
swipe(driver, start_x, item_center_y, end_x, item_center_y, duration_ms=300)
# Tap delete button (revealed after swipe)
delete_btn = driver.find_element(AppiumBy.ACCESSIBILITY_ID, "delete-item-btn")
delete_btn.click()
# Verify item is gone
items = driver.find_elements(AppiumBy.ACCESSIBILITY_ID, "list-item-0")
assert len(items) == 0
def test_pinch_to_zoom_map(android_driver):
"""Pinch-to-zoom on map view"""
driver = android_driver
# Uses Appium's W3C multi-touch actions for two-finger pinch
from selenium.webdriver.common.actions.pointer_input import PointerInput
size = driver.get_window_size()
center_x = size["width"] // 2
center_y = size["height"] // 2
# Two-finger pinch-out (zoom in):
finger1 = PointerInput(interaction.POINTER_TOUCH, "finger1")
finger2 = PointerInput(interaction.POINTER_TOUCH, "finger2")
actions = ActionBuilder(driver, mouse=finger1, keyboard=finger2)
# Start both fingers near center, move outward
actions.pointer_action.move_to_location(center_x - 50, center_y)
actions.pointer_action.pointer_down()
actions.pointer_action.move_to_location(center_x - 150, center_y)
actions.pointer_action.pointer_up()
actions.perform()Common Mistakes
- Hardcoding pixel coordinates — pixel positions change across device sizes; use percentages of screen dimensions or element.location for relative positions
- Swipe duration too fast — gestures that happen too fast aren't recognized; increase duration_ms to 500-1000ms for reliable swipe detection
- Not verifying gesture outcomes — tap the element and verify the expected outcome; don't assume the gesture succeeded just because no exception was thrown
- Using deprecated TouchAction API — Appium deprecated TouchAction; use W3C Actions API (ActionBuilder with PointerInput) for all new tests
Tip
Tip
Practice Gestures Touch Actions in small, isolated examples before integrating into larger projects. Breaking concepts into small experiments builds genuine understanding faster than reading alone.
Testing pyramid: many unit tests, fewer integration, fewest E2E
Practice Task
Note
Practice Task — (1) Write a working example of Gestures Touch Actions from scratch without looking at notes. (2) Modify it to handle an edge case (empty input, null value, or error state). (3) Share your solution in the Priygop community for feedback.
Quick Quiz
Common Mistake
Warning
A common mistake with Gestures Touch Actions is skipping edge case testing — empty inputs, null values, and unexpected data types. Always validate boundary conditions to write robust, production-ready software testing code.
Key Takeaways
- Mobile apps rely on native gestures — swipe, scroll, pinch, long-press, double-tap, drag — that have no equivalent in web testing.
- Hardcoding pixel coordinates — pixel positions change across device sizes; use percentages of screen dimensions or element.location for relative positions
- Swipe duration too fast — gestures that happen too fast aren't recognized; increase duration_ms to 500-1000ms for reliable swipe detection
- Not verifying gesture outcomes — tap the element and verify the expected outcome; don't assume the gesture succeeded just because no exception was thrown