how to run Appium tests on cloudf.one cloud phones
how to run Appium tests on cloudf.one cloud phones
if you want to run Appium tests on a cloudf.one cloud phone, the workflow is shorter than most teams expect. Appium speaks the W3C WebDriver protocol on top of UiAutomator2 for Android, and any device reachable over ADB is a valid Appium target. cloud phones are reachable over ADB-over-network out of the box, so a remote Appium server can drive a real Singapore Android device with the same code you would write for a USB-connected handset.
this tutorial walks the full setup: install platform-tools, connect to the cloud phone, install Appium, run a smoke test, and scale to parallel test execution. the same pattern applies to cucumber, pytest, mocha, or any Appium-compatible test runner.
why Appium on cloud phones beats the alternatives
three alternatives compete with Appium-on-cloud-phones. local emulators are fast but miss real-device-only behaviors (Play Integrity, biometric auth, real arm64 native code). USB-connected physical devices work but require lab maintenance and do not parallelize easily. paid device clouds (Sauce Labs, BrowserStack) work but charge per minute and lock you into proprietary toolchains.
Appium on cloud phones gives you the parallelization of a device cloud with the cost profile of a long-running lease. you write standard Appium code, run it locally against your own fleet, and scale by adding more cloud phones to the pool.
for context on cloud phone basics, see cloud phone vs physical Android device and how to set up ADB on cloudf.one.
prerequisites
on your laptop or CI runner: Java 17 or newer, Node.js 20 or newer, Android platform-tools (for adb), Python or Java for your test code (Appium supports clients in many languages).
# install platform-tools on macOS
brew install android-platform-tools
# install Node 20
brew install node@20
# install Appium and the UiAutomator2 driver
npm install -g appium
appium driver install uiautomator2
verify the install:
appium --version
appium driver list --installed
you should see uiautomator2 listed.
step 1: connect to your cloud phone
log in to cloudf.one and pick a phone tagged for testing. copy the ADB endpoint from the device details page; it looks like adb-sg.cloudf.one:5555 or a numeric IP:port.
adb connect adb-sg.cloudf.one:5555
adb devices
you should see one device listed with the endpoint as its serial. that serial is what Appium will use.
step 2: start the Appium server
in one terminal:
appium server --port 4723 --base-path /wd/hub
leave it running. it should print “Welcome to Appium v2.x” and “Appium REST http interface listener started on 0.0.0.0:4723.”
step 3: write a smoke test
create a Python file test_smoke.py:
from appium import webdriver
from appium.options.android import UiAutomator2Options
options = UiAutomator2Options()
options.platform_name = "Android"
options.device_name = "cloudf.one-sg-01"
options.udid = "adb-sg.cloudf.one:5555"
options.app_package = "com.android.settings"
options.app_activity = ".Settings"
options.no_reset = True
driver = webdriver.Remote("http://localhost:4723/wd/hub", options=options)
print("connected, current activity:", driver.current_activity)
driver.quit()
install the Python client:
pip install Appium-Python-Client
run the test:
python test_smoke.py
you should see “connected, current activity: .Settings” within a few seconds.
step 4: drive the UI
a more useful test taps a button and asserts a result. assume the cloud phone has a calculator app installed:
from appium.webdriver.common.appiumby import AppiumBy
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
options.app_package = "com.google.android.calculator"
options.app_activity = "com.android.calculator2.Calculator"
driver = webdriver.Remote("http://localhost:4723/wd/hub", options=options)
wait = WebDriverWait(driver, 10)
wait.until(EC.presence_of_element_located((AppiumBy.ID, "com.google.android.calculator:id/digit_2"))).click()
driver.find_element(AppiumBy.ID, "com.google.android.calculator:id/op_add").click()
driver.find_element(AppiumBy.ID, "com.google.android.calculator:id/digit_3").click()
driver.find_element(AppiumBy.ID, "com.google.android.calculator:id/eq").click()
result = driver.find_element(AppiumBy.ID, "com.google.android.calculator:id/result_final").text
assert result == "5"
driver.quit()
this is standard Appium against a real arm64 Android phone. no emulator quirks, no x86 translation layer.
step 5: parallelize across many cloud phones
for parallel execution, lock multiple cloud phones and start one Appium driver session per phone. the easiest pattern uses pytest-xdist or the built-in test parallelism of your test framework.
# pytest example with one device per worker
import os
import pytest
from appium import webdriver
from appium.options.android import UiAutomator2Options
@pytest.fixture
def driver():
udid = os.environ["DEVICE_UDID"] # injected per worker
options = UiAutomator2Options()
options.platform_name = "Android"
options.udid = udid
options.system_port = 8200 + int(os.environ["WORKER_ID"])
drv = webdriver.Remote("http://localhost:4723/wd/hub", options=options)
yield drv
drv.quit()
each worker gets a distinct UDID and a distinct system_port. cloudf.one’s REST API gives you a list of locked devices; map them to workers via environment variables.
step 6: handle test failures cleanly
on failure, capture screenshots and page source.
def pytest_exception_interact(node, call, report):
if report.failed:
driver = node.funcargs.get("driver")
if driver:
driver.save_screenshot(f"artifacts/{node.name}.png")
with open(f"artifacts/{node.name}.xml", "w") as f:
f.write(driver.page_source)
upload the artifacts via your CI’s artifact system; see our CI/CD integration guide for the GitHub Actions example.
common pitfalls
three issues bite teams new to Appium on cloud phones.
first, ADB connection drops. ADB-over-network occasionally times out on long-running test runs (over 30 min). add a heartbeat: adb -s $UDID shell echo ok every 60 seconds in a background goroutine.
second, UiAutomator2 server port conflicts. each parallel session needs a distinct systemPort and chromedriverPort. set them explicitly per worker.
third, app reset between tests. set noReset=true if you want fast runs against a pre-installed app. set fullReset=false to avoid uninstalling the app between sessions.
if you want to read more on detection mechanics, see real device vs emulator detection. cloud phones pass detection that would fail any emulator stack.
running Appium server in CI
in CI, run the Appium server inline with your test step:
appium server --port 4723 --log-level error &
APPIUM_PID=$!
sleep 5
pytest tests/
kill $APPIUM_PID
the Appium docs cover deployment patterns, including running Appium as a daemon and managing the driver-plugin lifecycle.
try Appium on a real Singapore cloud phone
register for a free trial to get one cloud phone for an hour. that’s enough time to run the calculator example, then port your own test suite. once confirmed, scale to a paid plan and run Appium tests against a real-device pool.
frequently asked questions
does Appium need a different driver for cloud phones vs USB phones?
no. UiAutomator2 driver works for both. the only difference is the UDID format (network endpoint instead of USB serial).
can I run iOS tests against cloudf.one phones?
cloudf.one is Android-first. for iOS Appium tests, look at AWS Device Farm or Firebase Test Lab. iOS support on cloud phones in general is harder because Apple does not allow Android-style ADB-over-network on real iPhones.
what about Appium 1.x?
Appium 2 is the supported major version. Appium 1.x is end-of-life as of 2023. all examples in this post use Appium 2 conventions (W3C capabilities, separate driver install).
how many parallel Appium sessions can one server handle?
one Appium 2 server can drive 5 to 10 parallel sessions on a modern laptop. for more parallelism, run multiple Appium server instances on different ports. CI runners typically run one Appium server per runner.
does Appium work over a slow network?
ADB-over-network adds 150 to 250 ms latency from outside Singapore. tests that wait for explicit elements work fine. tests that depend on tight timing (animations, scrolling inertia) should run from an Asia-Pacific runner. otherwise add explicit waits and avoid Thread.sleep shortcuts.