After 11 Robot Framework lessons, we already know what the .robot file contains, how keywords run, where variables are, and which log to read when a test fails. Now it's time to take out the phone and plug it into the computer.

Mobile testing is more annoying than browser automation in that there are quite a few layers standing in between the test case and the application. If any layer isn’t installed, has the wrong version, or points to the wrong path, Robot Framework will report an error on this side, while the real cause is all the way over at ADB on the other side. This article will build each layer one by one so that when an error occurs, you’ll know which layer to grab.

The baseline of the article was checked according to the documentation and package registry on 08/09/2026. The installation part has been verified with dry-run and build; the smoke flow still needs to be run on the real device before removing the draft status.

Android Studio Device Manager displays a list of virtual Android devices
Android Studio Device Manager displays a list of virtual Android devices

Device Manager is the place to create, boot, and manage AVDs. Image: Android Developers, used under Content License. This interface only illustrates the AVD flow, not a smoke test evidence of the project.

Six layers are talking to each other

Robot FrameworkAppiumLibraryAppium ServerUiAutomator2 DriverADBUSB Device or AndroidEmulator
// Mermaid diagram

Robot Framework reads test cases and orchestrates keywords. AppiumLibrary transforms keywords like Click Element into commands of the Appium Python Client. The client sends HTTP requests to the Appium Server. The server forwards commands to the UiAutomator2 Driver, and the driver uses ADB to install a helper server on Android and control the interface.

In short, Robot Framework does not know how to tap on the phone screen by itself. It delegates tasks through four intermediate layers. Therefore, pip install robotframework-appiumlibrary succeeding only means that the Python layer has completed, not that the phone is ready.

Version set used in the article

ComponentVersion used as baselineWhy needed
Python3.13.xRun Robot Framework and AppiumLibrary
Robot Framework7.4.2Test runner and keyword engine
AppiumLibrary3.2.1Connect Robot Framework with Appium Python Client
Node.js22.12 or aboveRun Appium 3 and UiAutomator2 Driver
npm10 or aboveInstall Appium and drivers
Appium3.7.0WebDriver server
UiAutomator2 Driver8.6.1Appium's Android driver
JDK17Build and run Android components that require Java
Android SDKLatest Platform Tools, API 35 for AVDProvide ADB, emulator, and Android platform

The version in the table is not an eternal truth. It is a set that has been finalized so that the later sections speak the same language. If you update Appium on your own but keep the old driver from three seasons ago, when an error occurs, don't rush to conclude that Robot Framework hates you.

Install Python dependencies in a virtual environment

Create the project folder, virtual environment, and activate it:

New-Item -ItemType Directory -Path robot-android
Set-Location robot-android
py -3.13 -m venv .venv
.\.venv\Scripts\Activate.ps1

If PowerShell blocks the activation script, just change the policy for the current terminal process:

Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
.\.venv\Scripts\Activate.ps1

You don’t need to change the whole machine’s policy just to enable a virtual environment. If you’re using a knife to peel fruit, don’t casually chop the whole table.

Create requirements.txt:

robotframework==7.4.2
robotframework-appiumlibrary==3.2.1

Then install and check:

python -m pip install --upgrade pip
python -m pip install -r requirements.txt
python --version
robot --version
python -m pip show robotframework-appiumlibrary

Using python -m pip helps ensure pip belongs to the correct Python in .venv. If you type a pip floating on the PATH, you might install the library into Python A, but VS Code and Robot Framework run Python B. He says chicken, she imports AppiumLibrary.

Install Node.js, Appium, and UiAutomator2

After installing Node.js 22, check:

node --version
npm --version

Install the correct Appium baseline, then install the separate Android driver:

npm install --global appium@3.7.0
appium driver install uiautomator2@8.6.1
appium --version
appium driver list --installed

Appium 3 does not automatically bring UiAutomator2 along. npm install --global appium only installs the server; appium driver install installs the thing that knows how to control Android. This is where newcomers often stop the earliest and then look at the Could not find a driver for automationName 'UiAutomator2' line with a hurt gaze.

Install JDK and Android SDK

Install JDK 17, then open Android Studio and go to Settings → Languages & Frameworks → Android SDK. In the SDK Manager, install:

Suppose the JDK is located at C:\Program Files\Eclipse Adoptium\jdk-17 and the Android SDK is located at %LOCALAPPDATA%\Android\Sdk, set the environment variable for the current user:

[Environment]::SetEnvironmentVariable(
    'JAVA_HOME',
    'C:\Program Files\Eclipse Adoptium\jdk-17',
    'User'
)

[Environment]::SetEnvironmentVariable(
    'ANDROID_HOME',
    "$env:LOCALAPPDATA\Android\Sdk",
    'User'
)

Add the necessary directories to Path using the Environment Variables interface of Windows:

%JAVA_HOME%\bin
%ANDROID_HOME%\platform-tools
%ANDROID_HOME%\emulator
%ANDROID_HOME%\cmdline-tools\latest\bin

Mở terminal mới rồi kiểm tra:

java -version
adb version
where.exe java
where.exe adb

JAVA_HOME points to the JDK directory, not directly to java.exe. ANDROID_HOME also points to the SDK directory, not to platform-tools. The new PATH is where you put the directories containing executables so that the terminal can find them.

Finally run doctor:

appium driver doctor uiautomator2

The goal is 0 required fixes needed. Optional fixes can be read later; if the required fix is still red, tests should not be written yet.

Method A: real Android device via USB

On your phone, open Settings → About phone, tap Build number multiple times until Developer Options are enabled. Go back to Developer Options and turn on USB debugging.

On Windows, some manufacturers require OEM USB drivers. If Device Manager only sees the phone as a media device or shows a warning icon, install the driver from the manufacturer. Don't download a universal-super-adb-driver-final-final.exe file from some dark corner of the Internet and hope for the best.

Plug in the cable with data transfer, unlock the phone, and accept the RSA dialog Allow USB debugging. Check:

adb devices -l

The good result takes the form:

List of devices attached
R58M123456A    device product:... model:... transport_id:1

The first part of the line is the serial. It will be used as udid:

$env:ANDROID_UDID = 'R58M123456A'
$env:ANDROID_TARGET = 'usb'

Capability deviceName does not select a device in UiAutomator2. When there are multiple targets, using udid is the correct way to specify the device that needs to be controlled.

Method B: Android Emulator in Android Studio

Open Tools → Device Manager → Create Virtual Device, select Pixel 7, system image API 35, and name the AVD Pixel_7_API_35. Start the AVD, wait until the home screen is usable, then check:

adb devices -l
adb -s emulator-5554 shell getprop sys.boot_completed

adb devices may appear as device before Android has finished booting. Only when the second command returns 1 should an Appium session be initiated.

Set the target for the terminal to run the Robot:

$env:ANDROID_AVD = 'Pixel_7_API_35'
$env:ANDROID_TARGET = 'avd'

If you plug in a real phone while opening the emulator, always pass udid or avd. Letting Appium choose the 'first device' is a game of chance that brings no reward.

Preparing ApiDemos

Download ApiDemos-debug.apk from the official AppiumLibrary sample repository and place it into demoapp\ApiDemos-debug.apk in the project. There are two ways to install:

adb -s $env:ANDROID_UDID install -r .\demoapp\ApiDemos-debug.apk

Or let Appium install using the app capability when opening a session. This article uses the second method so that a newly cloned project can prepare the app by itself.

First smoke test

Create smoke.robot:

*** Settings ***
Library    AppiumLibrary
Suite Teardown    Close All Applications

*** Variables ***
${APPIUM_URL}    http://127.0.0.1:4723
${APK}           ${CURDIR}${/}demoapp${/}ApiDemos-debug.apk
${TARGET}        %{ANDROID_TARGET=avd}
${UDID}          %{ANDROID_UDID=}
${AVD}           %{ANDROID_AVD=Pixel_7_API_35}

*** Test Cases ***
ApiDemos Can Open The Search Screen
    Open ApiDemos On The Selected Target
    Wait Until Page Contains Element
    ...    id=io.appium.android.apis:id/txt_query_prefill
    ...    timeout=10s
    Capture Page Screenshot    smoke-search.png

*** Keywords ***
Open ApiDemos On The Selected Target
    IF    $TARGET == 'usb'
        Open ApiDemos On A USB Device
    ELSE IF    $TARGET == 'avd'
        Open ApiDemos On An AVD
    ELSE
        Fail    ANDROID_TARGET must be 'usb' or 'avd', got '${TARGET}'
    END

Open ApiDemos On A USB Device
    Should Not Be Empty    ${UDID}    ANDROID_UDID is required for USB
    Open Application
    ...    ${APPIUM_URL}
    ...    platformName=Android
    ...    automationName=UiAutomator2
    ...    udid=${UDID}
    ...    app=${APK}
    ...    appPackage=io.appium.android.apis
    ...    appActivity=.app.SearchInvoke
    ...    autoGrantPermissions=${TRUE}

Open ApiDemos On An AVD
    Open Application
    ...    ${APPIUM_URL}
    ...    platformName=Android
    ...    automationName=UiAutomator2
    ...    avd=${AVD}
    ...    app=${APK}
    ...    appPackage=io.appium.android.apis
    ...    appActivity=.app.SearchInvoke
    ...    autoGrantPermissions=${TRUE}

The keyword coordinating selects the correct branch from ANDROID_TARGET. The USB branch must have ANDROID_UDID; the AVD branch uses the virtual device name. The two keywords Open Application are deliberately written in full in the installation article so that you can see where the capabilities differ. In the next article, this repeated section will be consolidated into a dictionary.

Open Application creates a WebDriver session on the Appium Server. platformName indicates this is Android, and automationName selects UiAutomator2. app points to the APK on the machine running Appium. appPackage and appActivity specify the application and the screen to open. autoGrantPermissions is suitable for the sample app; for a real product, it's still necessary to separately check the denial and permission granting flow.

Open the first terminal and run the server:

appium

Appium 3 listens by default at http://127.0.0.1:4723. It does not automatically add /wd/hub; that is the old style base path.

In the second terminal, activate .venv and then run:

robot --outputdir results .\smoke.robot

If the test passes, results will have output.xml, log.html, report.html and a screenshot. This is the milestone for 'setup running successfully', not the milestone for installing the package without any red warnings.

How are real devices and emulators different?

PartUSB deviceAndroid Emulator
Android SDK, ADB, JDK, AppiumSameSame
Target preparationUSB debugging, RSA, OEM driverSystem image, AVD, hardware acceleration
Stable identificationudid from serialavd=Pixel_7_API_35 or emulator serial
Reality closenessHas real hardware, OEM, and actual policiesClean environment, easy to reset and change API
Common errorsCable, driver, authorization, OEM policyBoot, snapshot, RAM, disk, GPU, hypervisor

Test logic should not know whether it is running on a USB or an AVD. The differences should only lie in the capability or variable file. In the next lesson, we will separate this part so that the same script can run on both.

Final checklist

python --version
robot --version
node --version
npm --version
java -version
adb version
adb devices -l
appium --version
appium driver list --installed
appium driver doctor uiautomator2

If all the above commands are fine and the smoke test can create a session, the setup is complete. If not, don't jump straight to fixing the XPath. XPath is not to blame in the adb case when it hasn't even seen the phone.

Verification status of the draft: Robot Framework syntax has been dry-run and the site has been built; the smoke flow has not been run on a real USB device or AVD in this iteration. Therefore, I keep draft: true, not pretending to turn a compile-able piece into proof that the device has successfully run it.

References