如何在 Android 设备上进行自动化测试
本文面向 在 PandaTest 上跑 Android 自动化 的场景:设备挂在 Agent 上、任务在测试容器里执行。你要解决的问题(连哪台机、脚本里怎么指设备、ADB 怎么用)是一回事。
1. 读完你能直接做的事
- 确认 Android 真机已被 Agent 识别并出现在控制台
- 在 构建套件 里选对设备,让任务跑到指定序列号上
- 在 pytest / uiautomator2 / Appium / Airtest 脚本里 正确读取当前任务分配的设备
- 理解 测试容器里 ADB 连的是宿主机,避免照抄本地
127.0.0.1踩坑 - 区分 多机并行(多任务) 与 单脚本里切换多台设备
2. 和「本地 IDE 直连手机」差在哪里
| 维度 | 本地 IDE(如插 USB 开 IDE) | PandaTest |
|---|---|---|
| 设备物理连接 | 本机 USB | Agent 机器 USB / 机房 |
| ADB | 本机 adb | Agent 宿主机 adb;任务在 Docker 里通过转发连这台 adb |
| 选哪台手机 | IDE 里点选 | 构建套件里 指定设备 或 过滤条件;平台下发 device_serial |
| 一次运行多台 | 自己开多进程/多线程 | 套件 并发 + 多 Task,每台设备一个 Task,各跑一份脚本进程 |
平台侧会把每台任务机上的 device_id(ADB 序列号) 传给执行器;测试镜像启动时环境变量里会有 DEVICE_ID,脚本应以此为准,而不是写死序列号。
3. 前置:设备必须先被 Agent 识别
- Agent 所在机器已安装并运行 ADB,手机开启 USB 调试并已授权。
- 在宿主机执行
adb devices能看到序列号(或ip:port形式的无线调试地址)。 - 控制台 设备中心 里该设备在线且可被占用。
详细接线步骤见 Android 设备接入;各品牌打开调试见 各品牌USB调试(侧栏可换品牌)。
若设备在 Agent 上显示 unauthorized 或未出现在 adb devices
中,任务会在连接阶段失败;先解决宿主机 ADB,再跑自动化。
4. 在平台上跑起来:构建套件 → 任务
- 在 自动化测试 → 构建套件 中配置脚本来源(Git / 上传包)、被测 APK、目标设备(具体设备 ID 或 Android 过滤条件)。
- 选择与环境镜像匹配的 测试框架(如 pytest,以及镜像内已集成的 uiautomator2、Appium、Airtest 等)。
- 点击 运行:平台为每台选中设备创建一个 Task,并调度到对应 Agent 上的测试容器执行。
更细的字段说明见 套件配置与运行;结果与日志见 测试结果与定位。
5. 脚本如何知道「当前是哪台手机」:环境变量
任务启动后,运行器会向进程注入(你可以在 任务详情 → 执行日志 里看到变量表):
| 变量 | 含义 |
|---|---|
DEVICE_ID | 当前 Task 绑定的设备序列号(ADB serial),脚本里应优先读它 |
PLATFORM | android |
DEVICE_NAME | 控制台中的设备名称(展示用) |
APP_PACKAGE | 套件里配置的应用包名(若有) |
TASK_ID / JOB_ID | 任务与作业 ID,用于日志或上报 |
TEST_TIMEOUT | 超时秒数 |
构建套件里 环境变量 区块的键值也会合并进来(不要与上面重名覆盖关键变量,除非你清楚后果)。
测试容器内的 ADB 与 Python 库
执行 Android 任务时,镜像内会配置 ADB 指向 Agent 宿主机(例如通过 host.docker.internal:5037 转发),与你在本机直接插手机时「adb server 在本机」一致,只是 server 在宿主机、client 在容器。
因此:
- Shell 里可直接
adb -s "$DEVICE_ID" shell ...(前提是镜像入口脚本已就绪,平台提供的runtest流程会先等待设备在线)。 - 不要在脚本里写死
127.0.0.1:5037指宿主机;若某框架必须写 URI,应使用环境变量或文档推荐的宿主机别名(与镜像内ADB_SERVER_SOCKET/ANDROID_ADB_SERVER_HOST一致)。
6. 在代码里连接设备
uiautomator2
import os
import uiautomator2 as u2
serial = os.environ["DEVICE_ID"]
d = u2.connect(serial)Appium(示例思路)
在 Desired Capabilities 里把 udid(或等价字段)设为 os.environ["DEVICE_ID"],app 若已由平台安装,可按你们项目约定只启动包名。
Airtest
容器内已注入与 ADB 相关的环境变量。连接字符串需指向 当前 ADB 可访问的那台 server,且 serial 为 DEVICE_ID,例如:
import os
device_id = os.environ["DEVICE_ID"]
host = os.environ.get("DEVICE_HOST", "host.docker.internal")
port = os.environ.get("DEVICE_PORT", "5037")
# 按你使用的 Airtest 版本调整 URI 格式
uri = f"android://{host}:{port}/{device_id}"
from airtest.core.api import connect_device
connect_device(uri)若使用 Android:/// 且仅一台设备已连接在当前 ADB 视图下,部分场景也可工作,但 多机并行时务必显式带 serial,避免连错机。
直接执行 ADB
import os
import subprocess
serial = os.environ["DEVICE_ID"]
subprocess.run(["adb", "-s", serial, "shell", "getprop", "ro.product.model"], check=True)平台镜像也支持在 shell 脚本中用 adb -s "$DEVICE_ID"。
7. 多机:并行 vs 单脚本多设备
- 并行跑多套用例:在构建套件里选多台设备、打开并发;平台会为 每台设备起一个独立 Task,各自进程里只有一个
DEVICE_ID。这是最常见、最稳的方式。 - 同一脚本逻辑要操作两台手机(例如互加好友):默认 一个 Task 只绑定一台设备;多机协作通常用 多个 Task 并行、拆分脚本,或在符合网络与安全策略的前提下在脚本内自行
adb connect第二台。不要假设「连上多台就会自动在所有设备上各跑一遍」。
8. 本地写脚本、云端跑:控件获取
在控制台用 控件获取 连上设备,边查 UI 树边写 Python(如 uiautomator2),再提交到 Git 或由套件拉取。见 控件获取。
9. 自查清单
- Agent 上
adb devices是否正常、序列号是否与控制台一致? - 构建套件里设备筛选是否过宽,导致 Task 跑到了非预期机型?
- 脚本是否写死了序列号或
127.0.0.1,而不是读DEVICE_ID? - 失败时看 任务详情 → 执行日志 里设备连接阶段是否已报错(超时、offline、未授权)?
下一步:按项目选 脚手架与模板 或自建 runtest.sh / pytest 入口,并把上述环境变量接进你们的 driver 初始化代码。