Android Uiautomator2 Python Wrapper完全指南:从安装到自动化测试全流程
1. 引言:告别繁琐的Android自动化测试
你是否还在为Android应用的自动化测试而烦恼?面对复杂的UI交互、不稳定的元素定位和冗长的测试脚本,你是否渴望一种更高效、更简洁的解决方案?Android Uiautomator2 Python Wrapper(以下简称uiautomator2)正是为解决这些痛点而生。作为一个强大的Python库,它封装了Android Uiautomator2的核心功能,让你能够用简洁的Python代码实现复杂的Android自动化测试。
读完本文,你将获得:
- 从环境搭建到脚本编写的全流程掌握
- 高效定位和操作UI元素的技巧
- 处理复杂手势和场景的实战经验
- 构建稳定可靠的自动化测试框架的方法
2. 核心概念与工作原理
2.1 Uiautomator2是什么?
Uiautomator2是Google提供的一个Android UI自动化测试框架,基于Accessibility服务,允许开发者与设备上的任何应用进行交互。而uiautomator2 Python Wrapper则是对这一框架的Python封装,通过HTTP RPC服务将设备端的功能暴露给Python客户端,实现了用Python控制Android设备的能力。
2.2 核心优势
uiautomator2相比其他自动化测试工具具有以下显著优势:
| 特性 | uiautomator2 | Appium | Espresso |
|---|---|---|---|
| 运行速度 | ⚡⚡⚡ (毫秒级响应) | ⚡ (秒级响应) | ⚡⚡ (较快) |
| 环境依赖 | 少 (仅需ADB) | 多 (Java, Node.js等) | 多 (Android SDK) |
| 易用性 | 高 (Python API简洁) | 中 (需学习复杂配置) | 低 (Java/Kotlin开发) |
| 跨平台支持 | 仅Android | Android/iOS | 仅Android |
| 社区活跃度 | 高 | 高 | 中 |
3. 环境搭建与安装
3.1 系统要求
- Android设备:Android 4.4+ (API level 19+)
- Python环境:Python 3.8+
- 开发计算机:Windows/macOS/Linux
3.2 安装步骤
3.2.1 安装Python库
pip install -U uiautomator2
验证安装是否成功:
uiautomator2 --help
3.2.2 安装UI检查工具
uiautomator2推荐使用uiauto.dev作为UI元素检查工具:
pip install uiautodev
uiauto.dev
打开浏览器访问 https://uiauto.dev 即可查看设备当前界面结构。
3.3 设备连接配置
确保Android设备已开启"开发者选项"和"USB调试",并通过USB连接到计算机。验证设备连接:
adb devices
应输出类似以下内容:
List of devices attached
123456f device
4. 快速入门:第一个自动化脚本
4.1 连接设备
import uiautomator2 as u2
# 连接设备
d = u2.connect('123456f') # '123456f'是设备序列号,可通过adb devices获取
# 或如果只有一个设备连接,可简化为
d = u2.connect()
# 验证连接
print(d.info)
成功连接后,将输出设备基本信息,如:
{
'currentPackageName': 'com.android.launcher',
'displayHeight': 1920,
'displayRotation': 0,
'displaySizeDpX': 411,
'displaySizeDpY': 731,
'displayWidth': 1080,
'productName': 'OnePlus5',
'screenOn': True,
'sdkInt': 27,
'naturalOrientation': True
}
4.2 基本操作示例
以下是一个简单的自动化脚本,演示了启动应用、点击按钮和输入文本的基本操作:
import uiautomator2 as u2
# 连接设备
d = u2.connect()
# 设置隐式等待时间(默认20秒)
d.implicitly_wait(10.0)
# 启动计算器应用
d.app_start("com.android.calculator2")
# 确保应用已启动
d.wait_activity(".Calculator", timeout=10)
# 执行2 + 3 = 5的计算
d(text="2").click()
d(text="+"").click()
d(text="3").click()
d(text="=").click()
# 验证结果
result = d(resourceId="com.android.calculator2:id/result").get_text()
print(f"计算结果: {result}") # 应输出"5"
# 关闭应用
d.app_stop("com.android.calculator2")
5. 设备连接与管理
5.1 连接方式
uiautomator2支持多种设备连接方式:
5.1.1 USB连接
# 通过设备序列号连接
d = u2.connect('123456f') # 设备序列号可通过adb devices获取
# 如果只有一个设备连接,可简化
d = u2.connect()
5.1.2 网络连接
首先确保设备与计算机在同一网络,然后通过IP连接:
d = u2.connect('192.168.1.100:5555')
5.2 设备信息获取
# 获取基本设备信息
print(d.info)
# 获取详细设备信息
print(d.device_info)
# 获取屏幕尺寸
width, height = d.window_size()
print(f"屏幕尺寸: {width}x{height}")
# 获取WLAN IP
print(f"WLAN IP: {d.wlan_ip}")
# 获取设备序列号
print(f"设备序列号: {d.serial}")
示例输出:
{
'serial': '123456f',
'sdk': 27,
'brand': 'OnePlus',
'model': 'OnePlus5',
'arch': 'arm64-v8a',
'version': 8.1
}
屏幕尺寸: 1080x1920
WLAN IP: 192.168.1.100
设备序列号: 123456f
6. UI元素定位与操作
6.1 Selector选择器
uiautomator2提供了强大的Selector机制来定位UI元素,支持多种属性匹配:
# 通过文本定位
d(text="设置").click()
# 通过资源ID定位
d(resourceId="com.android.settings:id/search").click()
# 通过类名定位
d(className="android.widget.Button").click()
# 组合条件定位
d(text="设置", className="android.widget.TextView").click()
支持的选择器属性包括:
| 属性 | 描述 | 示例 |
|---|---|---|
| text | 元素文本 | text="设置" |
| textContains | 文本包含 | textContains="设" |
| textMatches | 文本正则匹配 | textMatches="^设.*" |
| resourceId | 资源ID | resourceId="com.android.settings:id/title" |
| className | 类名 | className="android.widget.TextView" |
| description | 元素描述 | description="搜索" |
| checkable | 是否可勾选 | checkable=True |
| checked | 是否已勾选 | checked=True |
| clickable | 是否可点击 | clickable=True |
| enabled | 是否启用 | enabled=True |
6.2 XPath定位
除了Selector,uiautomator2还支持XPath定位,特别适合复杂UI结构:
# 基本XPath定位
d.xpath("//android.widget.TextView[@text='设置']").click()
# 相对路径定位
d.xpath("//*[@resource-id='com.android.settings:id/list']/android.widget.LinearLayout[2]").click()
# 文本包含定位
d.xpath("//*[contains(@text, '无线')]").click()
# 获取所有匹配元素
elements = d.xpath("//android.widget.Button").all()
for element in elements:
print(element.get_text())
6.3 常用元素操作
# 点击操作
d(text="设置").click()
# 长按操作
d(text="设置").long_click()
# 获取文本
text = d(text="设置").get_text()
# 设置文本
d(resourceId="com.android.settings:id/search").set_text("WiFi")
# 清空文本
d(resourceId="com.android.settings:id/search").clear_text()
# 判断元素是否存在
exists = d(text="设置").exists
print(f"元素是否存在: {exists}")
# 等待元素出现
d(text="设置").wait(timeout=10) # 等待10秒
# 滑动查找元素
d(text="关于手机", scrollable=True).click()
7. 手势操作与交互
7.1 基本手势
# 点击屏幕坐标
d.click(500, 1000) # (x, y)
# 双击
d.double_click(500, 1000)
# 长按
d.long_click(500, 1000, duration=2) # 长按2秒
# 滑动
d.swipe(100, 500, 900, 500, duration=0.5) # 从左到右滑动,持续0.5秒
# 拖动
d.drag(100, 500, 900, 500, duration=1) # 拖动操作,持续1秒
7.2 扩展滑动操作
from uiautomator2 import Direction
# 向指定方向滑动
d.swipe_ext(Direction.RIGHT) # 向右滑动
d.swipe_ext(Direction.UP) # 向上滑动
# 控制滑动距离比例
d.swipe_ext(Direction.DOWN, scale=0.8) # 向下滑动,距离为屏幕高度的80%
# 在指定区域内滑动
d.swipe_ext(Direction.LEFT, box=(0, 0, 500, 500)) # 在左上角区域向左滑动
7.3 多点触控与手势
# 两点缩放(如图片放大)
d pinch_in() # 缩小
d pinch_out() # 放大
# 自定义手势路径
d.swipe_points([(100, 1000), (300, 800), (500, 1000), (700, 800), (900, 1000)], 0.5)
8. 应用管理
8.1 应用安装与卸载
# 安装应用(支持本地APK或URL)
d.app_install("https://example.com/app.apk")
# 卸载应用
d.app_uninstall("com.example.app")
# 强制停止应用
d.app_stop("com.example.app")
# 清除应用数据
d.app_clear("com.example.app")
8.2 应用启动与切换
# 启动应用
d.app_start("com.example.app")
# 启动应用并等待
d.app_start("com.example.app", wait=True)
# 启动指定Activity
d.app_start("com.example.app", ".MainActivity")
# 获取当前前台应用
current_app = d.app_current()
print(f"当前应用: {current_app['package']}")
# 等待应用启动
pid = d.app_wait("com.example.app") # 返回应用PID
if pid == 0:
print("应用启动失败")
else:
print(f"应用启动成功,PID: {pid}")
8.3 应用信息获取
# 获取应用信息
app_info = d.app_info("com.example.app")
print(f"应用版本: {app_info['versionName']}")
print(f"版本号: {app_info['versionCode']}")
# 获取所有已安装应用
all_apps = d.app_list()
print(f"已安装应用数量: {len(all_apps)}")
# 获取所有运行中应用
running_apps = d.app_list_running()
print(f"运行中应用: {running_apps}")
9. 高级功能
9.1 监控与处理弹窗(Watcher)
自动化测试中,弹窗(如广告、权限请求)常常干扰测试流程。uiautomator2的Watcher功能可以自动监控并处理这些弹窗:
# 创建Watcher
d.watcher("ALERT").when(text="允许").click()
d.watcher("ALERT").when(text="取消").click()
# 启动所有Watcher
d.watcher.start()
# 启动Watcher并设置触发次数限制
d.watcher.start(2) # 最多触发2次
# 检查是否有Watcher被触发
if d.watcher("ALERT").triggered:
print("ALERT Watcher已触发")
# 重置Watcher
d.watcher.reset()
# 停止所有Watcher
d.watcher.stop()
9.2 截图与屏幕录制
# 截取屏幕并保存
d.screenshot("screenshot.jpg")
# 获取PIL Image对象
image = d.screenshot()
image.save("screenshot.png")
# 屏幕录制
d.screenrecord("recording.mp4")
# 录制10秒后停止
import time
time.sleep(10)
d.screenrecord.stop()
9.3 处理Toast消息
# 获取最新Toast消息
toast = d.last_toast
print(f"最新Toast: {toast}")
# 等待特定Toast出现
if d.wait_for_toast("操作成功", timeout=5):
print("Toast出现")
else:
print("Toast未出现")
9.4 键盘与输入
# 发送文本(需先聚焦输入框)
d.send_keys("hello world")
# 发送按键事件
d.press("back") # 返回键
d.press("home") # Home键
d.press("volume_up") # 音量加
d.press("power") # 电源键
# 长按按键
d.long_press("power") # 长按电源键
# 设置剪贴板内容
d.set_clipboard("hello clipboard")
# 获取剪贴板内容
clipboard = d.clipboard
print(f"剪贴板内容: {clipboard}")
10. 测试框架集成与实战
10.1 与pytest集成
uiautomator2可以无缝集成pytest,构建强大的自动化测试框架:
# test_app.py
import uiautomator2 as u2
import pytest
@pytest.fixture(scope="module")
def d():
# 连接设备
d = u2.connect()
# 设置隐式等待时间
d.implicitly_wait(10.0)
# 启动应用
d.app_start("com.example.app")
yield d
# 测试结束后停止应用
d.app_stop("com.example.app")
def test_login(d):
# 输入用户名密码
d(resourceId="com.example.app:id/et_username").set_text("testuser")
d(resourceId="com.example.app:id/et_password").set_text("password123")
# 点击登录按钮
d(resourceId="com.example.app:id/btn_login").click()
# 验证登录成功
assert d(text="欢迎回来,testuser").exists
def test_logout(d):
# 点击个人中心
d(resourceId="com.example.app:id/nav_profile").click()
# 点击退出登录
d(text="退出登录").click()
# 确认退出
d(text="确定").click()
# 验证登录页面
assert d(resourceId="com.example.app:id/btn_login").exists
运行测试:
pytest test_app.py -v
10.2 处理复杂场景
10.2.1 滑动列表查找元素
def find_and_click(d, text):
"""滑动查找文本并点击"""
max_swipes = 5
swipe_count = 0
while swipe_count < max_swipes:
if d(text=text).exists:
d(text=text).click()
return True
# 向上滑动
d.swipe_ext(Direction.UP)
swipe_count += 1
return False
# 使用示例
find_and_click(d, "关于手机")
10.2.2 处理动态加载内容
def wait_for_element(d, **kwargs):
"""等待元素出现"""
timeout = d.settings['wait_timeout']
start_time = time.time()
while time.time() - start_time < timeout:
if d(** kwargs).exists:
return True
time.sleep(0.5)
return False
# 使用示例
if wait_for_element(d, text="加载完成"):
print("内容加载完成")
else:
print("内容加载超时")
10.3 测试报告生成
结合pytest-html插件生成美观的测试报告:
pip install pytest-html
pytest test_app.py -v --html=report.html --self-contained-html
11. 性能优化与最佳实践
11.1 提高元素定位稳定性
# 设置隐式等待时间
d.implicitly_wait(10.0) # 全局设置,单位秒
# 显式等待元素
d(text="设置").wait(timeout=5) # 等待5秒
# 使用资源ID定位(比文本定位更稳定)
d(resourceId="com.android.settings:id/title", text="设置").click()
11.2 减少不必要的等待
# 不好的实践
time.sleep(5) # 固定等待5秒,可能导致测试缓慢
# 好的实践
d.wait_activity(".MainActivity", timeout=10) # 等待特定Activity
if d(text="加载中").exists:
d(text="加载完成").wait() # 仅在需要时等待
11.3 异常处理
try:
d(text="设置").click()
except UiObjectNotFoundError:
print("未找到设置按钮,尝试滑动查找")
d.swipe_ext(Direction.UP)
d(text="设置").click()
except Exception as e:
print(f"发生异常: {str(e)}")
d.screenshot("error.png") # 捕获异常时截图
11.4 代码组织最佳实践
project/
├── common/ # 公共工具函数
│ ├── __init__.py
│ ├── element_finder.py # 元素查找工具
│ └── gesture_utils.py # 手势操作工具
├── pages/ # Page Object模型
│ ├── __init__.py
│ ├── login_page.py # 登录页面
│ └── home_page.py # 首页
├── tests/ # 测试用例
│ ├── __init__.py
│ ├── test_login.py
│ └── test_home.py
└── conftest.py # pytest配置
12. 常见问题与解决方案
12.1 元素定位失败
问题:明明存在的元素却定位不到。
解决方案:
- 检查元素属性是否动态变化(如resourceId包含随机数)
- 确保元素在当前界面可见,可能需要滑动
- 尝试不同的定位方式(如从文本定位改为资源ID定位)
- 增加等待时间或使用显式等待
# 解决动态元素问题
d.xpath("//*[contains(@resourceId, 'dynamic_id_')]").click()
# 确保元素可见
if not d(text="按钮").exists:
d.swipe_ext(Direction.UP)
d(text="按钮").click()
12.2 测试脚本不稳定
问题:相同的脚本有时成功有时失败,不稳定。
解决方案:
- 避免使用坐标操作,尽量使用UI属性
- 增加关键步骤的验证
- 使用Watcher处理意外弹窗
- 减少测试用例间的依赖
# 增加验证步骤
d(text="登录").click()
assert d(text="欢迎").exists, "登录失败"
12.3 处理权限请求
问题:Android 6.0+需要动态权限申请,干扰自动化流程。
解决方案:
- 测试前手动授予所有必要权限
- 使用ADB命令授予权限
- 结合Watcher自动处理权限弹窗
# 使用ADB授予权限
d.shell("pm grant com.example.app android.permission.CAMERA")
d.shell("pm grant com.example.app android.permission.WRITE_EXTERNAL_STORAGE")
# 使用Watcher自动处理
d.watcher("PERMISSION").when(text="允许").click()
d.watcher.start()
13. 总结与展望
13.1 核心优势回顾
uiautomator2作为Android自动化测试工具,凭借其毫秒级响应速度、简洁的Python API和强大的UI交互能力,为开发者提供了高效便捷的自动化测试解决方案。无论是快速原型验证、回归测试还是持续集成,uiautomator2都能显著提高测试效率,降低维护成本。
13.2 进阶学习资源
- 官方文档:uiautomator2 GitHub
- 元素定位工具:uiauto.dev
- 设备管理平台:atxserver2
- 测试报告生成:pytest-html
13.3 未来展望
随着Android系统的不断更新,uiautomator2也在持续进化。未来可能的发展方向包括:
- 更好地支持Android 12+的新特性
- 增强AI驱动的元素识别能力
- 与更多CI/CD工具集成
- 提供更丰富的测试报告和分析功能
掌握uiautomator2,让Android自动化测试变得简单而高效。现在就动手尝试,体验自动化测试的魅力吧!
14. 常见问题FAQ
Q1: uiautomator2支持iOS设备吗?
A1: 不支持。iOS自动化测试可考虑facebook-wda。
Q2: 如何处理WebView中的元素?
A2: uiautomator2对WebView支持有限,复杂场景建议结合Selenium或Appium。
Q3: 测试过程中如何记录日志?
A3: 可使用Python内置logging模块,或uiautomator2的调试模式:
d.debug = True # 启用调试模式,打印HTTP请求信息
Q4: 如何实现多设备并行测试?
A4: 可结合pytest-xdist实现分布式测试,或使用atxserver2进行设备管理。
Q5: uiautomator2与Appium如何选择?
A5: 简单场景、追求速度选uiautomator2;跨平台需求、复杂WebView场景选Appium。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



