Appium-based framework for testing iOS and Android apps
I built this to automate mobile app testing across both iOS and Android. Mobile testing has its own challenges (gestures, device differences, slower execution), so I focused on making the framework handle those gracefully.
Mobile isn't just "small screen web testing." There are unique challenges:
Gestures matter - Swipe, pinch, long-press are first-class interactions Slower execution - Devices/simulators are slower than browsers Platform differences - iOS and Android work differently Device fragmentation - Many screen sizes, OS versions App state - Apps can be backgrounded, killed, or interrupted
This framework handles these realities.
You'll need:
- Python 3.8+
- Appium Server -
npm install -g appium - Platform-specific tools:
- Android: Android SDK, ADB
- iOS: Xcode (Mac only)
# Clone
git clone https://github.com/JasonTeixeira/Mobile-Testing-Framework.git
cd Mobile-Testing-Framework
# Virtual environment
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
# Dependencies
pip install -r requirements.txt# In a separate terminal
appium
# Should see: "Appium REST http interface listener started on 0.0.0.0:4723"# Make sure your app path is set in config/capabilities.yaml
pytest tests/
# Or specific platform
pytest tests/android/
pytest tests/ios/Handles the differences between iOS and Android:
from framework.core.driver_manager import DriverManager
# Create Android driver
manager = DriverManager()
driver = manager.create_android_driver(
app_path="/path/to/app.apk",
device_name="Pixel 6"
)
# Or iOS
driver = manager.create_ios_driver(
app_path="/path/to/app.app",
device_name="iPhone 14"
)The manager reads capabilities from config/capabilities.yaml so you're not hardcoding settings.
Mobile interactions are different from web. The BasePage class handles mobile-specific stuff:
from framework.core.base_page import BasePage
page = BasePage(driver)
# Mobile gestures
page.swipe_up() # Scroll down
page.swipe_left() # Navigate carousel
page.scroll_to_element(...) # Scroll until element visible
page.long_press(...) # Context menu
# App management
page.background_app(5) # Send to background for 5 seconds
page.hide_keyboard() # Dismiss keyboard
# Normal stuff still works
page.tap(locator)
page.send_keys(locator, "text")
page.get_text(locator)Mobile-Testing-Framework/
├── framework/
│ ├── core/
│ │ ├── driver_manager.py # iOS/Android driver setup
│ │ └── base_page.py # Mobile interactions & gestures
│ ├── pages/ # Page objects
│ └── utils/ # Helper utilities
│
├── tests/
│ ├── android/ # Android-specific tests
│ ├── ios/ # iOS-specific tests
│ └── conftest.py # Pytest fixtures
│
├── config/
│ └── capabilities.yaml # Device/app configuration
│
├── screenshots/ # Failure screenshots
├── logs/ # Test logs
└── requirements.txt # Dependencies
Edit config/capabilities.yaml for your app:
android:
app: /path/to/your/app.apk
deviceName: Android Emulator
platformVersion: "13.0"
# Or test installed app
appPackage: com.your.app
appActivity: .MainActivity
ios:
app: /path/to/your/app.app
deviceName: iPhone 14
platformVersion: "16.0"
# Or test installed app
bundleId: com.your.appKeep tests clean by using page objects:
# framework/pages/login_page.py
from framework.core.base_page import BasePage
from appium.webdriver.common.appiumby import AppiumBy
class LoginPage(BasePage):
# Locators
USERNAME = (AppiumBy.ID, "com.app:id/username")
PASSWORD = (AppiumBy.ID, "com.app:id/password")
LOGIN_BTN = (AppiumBy.ID, "com.app:id/login")
def login(self, username, password):
self.send_keys(self.USERNAME, username)
self.send_keys(self.PASSWORD, password)
self.hide_keyboard() # Mobile-specific!
self.tap(self.LOGIN_BTN)# tests/android/test_login.py
import pytest
from framework.pages.login_page import LoginPage
def test_login(android_driver):
login_page = LoginPage(android_driver)
login_page.login("user@test.com", "password")
# Assert something
assert login_page.is_element_present(...)Mobile elements can be tricky:
- Use Appium Inspector to find correct locators
- Elements might be off-screen - try scrolling to them
- Wait for elements with
wait_for_element_visible()
Yes, mobile tests are slower than web:
- Real devices are faster than emulators
- Run tests in parallel:
pytest -n 4 - Use
noReset: trueto avoid reinstalling app each time
That's reality. You might need platform-specific tests:
@pytest.mark.android
def test_android_specific():
...
@pytest.mark.ios
def test_ios_specific():
...Mobile tests can be flaky:
- Use explicit waits (not fixed sleep)
- Handle unexpected alerts/popups
- Screenshot on failure to debug
- Use
pytest-rerunfailuresto auto-retry
✅ Use explicit waits everywhere ✅ Test on real devices when possible ✅ Handle both portrait and landscape ✅ Test with poor network conditions ✅ Clean up test data between runs
❌ Use time.sleep() - use WebDriverWait instead
❌ Hardcode element coordinates
❌ Test only on latest OS version
❌ Ignore platform differences
❌ Run tests on your only device (get a test device!)
- Install Android Studio
- Setup Android SDK
- Enable developer options on device/emulator
- Verify:
adb devicesshows your device
- Install Xcode
- Install Xcode Command Line Tools
- For real devices: Register device UDID with Apple
- Verify:
xcrun simctl listshows simulators
Check your setup:
npm install -g appium-doctor
appium-doctor --android
appium-doctor --iosFix any red ❌ items it finds.
Building this taught me:
About Mobile Testing:
- Mobile gestures are more complex than they seem
- iOS and Android locators work differently
- Real devices behave differently than emulators
- App lifecycle (background, foreground, killed) matters
About Appium:
- It's slower than Selenium (that's normal)
- Element location strategies differ by platform
- XPath is slow on mobile - use IDs when possible
- Session management is tricky with multiple devices
About Test Design:
- Parallel execution saves time but needs isolated tests
- Screenshots are critical for debugging failures
- Test data cleanup matters more on mobile
- Network conditions affect behavior significantly
"Could not start a new session"
- Is Appium server running? (
appiumin terminal) - Check logs at
~/.appium/logs
"App not installed"
- Verify app path in capabilities.yaml
- For Android: Check if app is signed
- For iOS: Check provisioning profile
"Session not created"
- Device might be locked
- Wrong platform version specified
- USB debugging not enabled (Android)
Found bugs or improvements? Open an issue or PR!
Jason Teixeira
- GitHub: @JasonTeixeira
- Email: sage@sageideas.org
MIT License - use however you want.
I chose Appium because:
- Cross-platform - One framework for iOS and Android
- Open source - No licensing costs
- Standard WebDriver - If you know Selenium, you know Appium
- Active community - Lots of support and documentation
It's not perfect (slower than native tools), but the cross-platform benefit is huge.