uiautomator2 对比 Appium
Appium:相对复杂,要安装的软件多,程序需要配置启动参数
Appium 适用场景:跨平台,标准化
uiautomator2:相对简单,适合入门学习
环境搭建
Python 3
Pycharm 社区版
一台安卓设备(可以是安卓模拟器)
入门操作
入门示例程序代码如下:
# 导入库文件 重命为uiout2
import uiautomator2 as uiout2
# 连接设备 (通过设备序列号,测试设备只有一台时序列号可以为空)
device = uiout2.connect()
# 打开被测试的应用程序
device(text='QQ').click()
adb 安装
adb
全称安卓调试桥,调试安卓设备必备工具
adb 安装与配置
连接设备
连接设备:
查看设备序列号:使用命令行命令 adb devices
通过设备序列号,测试设备只有一台设备时序列号可以为空
通过设备的IP地址进行连接时,需要打开设备的开发者模式网络调试开关
相关代码示例如下:
# 导入库文件 重命为uiout2
import uiautomator2 as uiout2
# 连接设备 设备序列号:33QY6790
device = uiout2.connect('33QY6790')
# 连接设备 设备:http://192.168.0.12
device = uiout2.connect('http://192.168.0.12')
# 连接设备 设备:http://192.168.0.13:3321
device = uiout2.connect('http://192.168.0.13:3321')
# 连接设备 设备IP:192.168.0.13
device = uiout2.connect_wifi('192.168.0.13')
应用的安装与卸载
安装应用
准备一个应用安装包地址,可以是网络地址也可以是本地地址
# 安装 应用
url = 'https://p.baifi.com/89uui/qiyu/qq.apk'
device.app_install(url)
获取应用包名
获取应用包名前提需要打开应用,返回内容里 package
后面的是包名
# 导入库文件 重命为uiout2
import uiautomator2 as uiout2
# 连接设备 adb 通过设备序列号: 33QY6790
device = uiout2.connect(r'33QY6790')
# 打开应用
device(text='QQ').click()
# 获取当前屏幕上正在运行应用的包名
print(device.app_current())
# 获取所有正在运行应用的包名
print(device.app_list_running())
通过 adb
指令获取应用包名,前提需要打开应用
两条命令结果一样
adb shell dumpsys activity | find "mFocusedActivity"
adb shell dumpsys activity top | findstr ACTIVITY
通过 aapt
指令获取应用包名,前提需要配置 aapt
环境变量
解析得到应用包名,file.apk
为文件或者包含文件的路径
aapt dump badging file.apk
卸载应用
调用方法 传入应用包名
device.app_uninstall()
应用的启动和关闭
应用的启动
前提是需要获取应用包名,通过包名启动应用
device.app_start()
应用的关闭
前提是需要获取应用包名,通过包名启动应用
device.app_stop()
清楚应用数据
清空输入框等控件数据,需要传入应用包名
device.app_clear()
设备操作
获取设备信息
获取设备相关信息,返回为字典类型 两行方法执行结果不太一样,请尝试一下
device.info
device.device_info
获取设备屏幕大小
获取设备屏幕大小,方便定位元素
返回为元组类型
device.window_size()
设备截屏
在设备上截取屏幕信息,传入参数为文件名:1.png
;也可以传入保存路径
device.screenshot('1.png')
device.screenshot(r'c:\1.png')
推送文件到设备
从电脑把文件传送到目标设备,传入第一个参数为电脑文件保存路径;第二个参数为设备存放文件地址 可能遇到权限问题
device.push(r'c:\1.xls', '/data/')
从设备拉取文件到电脑
把设备文件传送到电脑,传入第一个参数为设备存放文件地址;第二个参数为电脑文件保存路径 可能遇到权限问题
device.pull('/data/1.xls', r'c:\1.xls')
设备按键操作
模拟设备按键操作,不需要物理按键支持,也可以使用数字
# 设备亮屏
device.screen_on()
# 设备熄屏
device.screen_off()
# 主页
device.press("home")
# 返回
device.press("back")
# 左键
device.press("left")
# 右键
device.press("right")
# 上
device.press("up")
# 下
device.press("down")
# 中
device.press("center")
# 菜单
device.press("menu")
# 搜索
device.press("search")
# 回车
device.press("enter")
# 删除
device.press("delete ( or del)")
# 最近浏览应用
device.press("recent (recent apps)")
# 拍照
device.press("camera")
# 电源
device.press("power")
相关示例代码:
# 音量加
device.press("volume_up")
# 音量减
device.press("volume_down")
# 静音
device.press("volume_mute")
# 最近浏览应用
device.press('recent')
# 电源键
device.press('power')
元素定位
通过某一些特征来定位元素,辅助元素定位工具 weditor
python 3
在安装 weditor
时 报错请参考1 报错请参考2
安卓官方有一个工具 uiautomatorviewer
,安卓 sdk
(sdk/tools/uiautomatorviewer.bat
)自带
推荐使用 weditor
应用页面是用 xml
文件构成
# 安装 weditor
pip install weditor
# 查看已经安装的库
pip list
# 运行weditor
weditor
# 通过 text 文本定位 进入应用
device(text='').click()
# 通过 resourceId 定位
device(resourceId="").click()
# 通过 textContains (text 通过文本内容包含内容 定位元素,要确保唯一性)
device(textContains="").click()
# 通过 className 文本定位
device(className='').click()
# device(参数1,参数2).click() 组合条件
device(className='', text='').click()
# 当通过 特征 定位的元素有多个时,可以通过 索引(从0开始)选择已经定位的元素
device(resourceId="", instance=2).click()
# 通过 层级关系定位
device(resourceId='').child(resourceId='').click()
# 通过相对位置定位元素 速度较慢
button = device(resourceId='')
button.left().click()
元素构成
1.控件名称
2.控件属性
3.子控件
根据层级关系定位元素
-
上下级关系 (包含与被包含)
-
同胞关系 (同级关系)
-
相对位置 (左右上下)
设备应用操作
点击
首先需要连接设备
# 通过 元素特征定位来点击
device(resourceId='').click()
# 通过 元素坐标来点击
device.click(x, y)
# 通过 元素位置百分比来点击
device.click(w, h)
滑动操作
在设备屏幕上进行滑动操作
# 4个参数:开始坐标x,y;结束坐标x,y
device.swipe(startx, starty, endx, endy)
# 2个参数:滑动方向;滑动幅度
device.swipe('', scale=)
# 通过元素定位滑动 2个参数:滑动方向;滑动时长
device.swipe('', steps=)
内容输入和清空
在设备上模拟输入和清空输入内容
# 定位输入框 输入内容
device(resourceId='').send_keys('')
# 清空输入内容
device(resourceId='').clear_text()
获取设备截屏
获取设备屏幕信息图片,图片处理库 pillow
以及 cv2
# 获取设备屏幕信息图片 默认保存在当前程序文件夹
device.screenshot('')
# 获取设备屏幕信息图片 可以接入图片处理库 pillow ,需要导入库文件
image = device.screenshot()
# 保存图片
image.save("")
# 图片模糊处理 并保存
image.filter(ImageFilter.BLUR).save()
# 图片大小处理 并保存
image.resize((w, h)).save()
元素等待
代码执行快,设备相应较慢,所以需要等待
# 普通等待 (浪费时间)
time.sleep()
# 等应用加载完成启动 默认20秒内,超时报错(智能等待)
device.app_start('', wait=True)
# 修改默认wait值 (这一行应该在上面)
device.wait_timeout =
# 修改默认wait值 (这一行应该在上面 全局设置)
device.implicitly_wait()
# 等待页面加载完成
device.wait_activity()
# 等待元素出现 返回布尔值(True/False)
device(text='').wait()
# 等待元素消失 返回布尔值(True/False)
device(text='').wait_gone()
# 判断等待元素是否存在
device(text='').exist()
# 可以通过 timeout 设置等待时间 (单独设置)
device(resourceId="").click(timeout=)
# 输入等待 通过 timeout 设置等待时间 (单独设置)
device(resourceId="").set_text(timeout=)
# 清空等待 通过 timeout 设置等待时间 (单独设置)
device(resourceId="").clear_text(timeout=)
实际案例及源码
测试一个应用的登录功能
- 测试用例:制定合适的测试参数及相关操作方案
- 代码编写:编写测试代码
- 断言:写测试总结
基本源码
# 导入库文件 重命为uiout2
import uiautomator2 as uiout2
# 连接设备 (通过设备序列号,测试设备只有一台时序列号可以为空)
device = uiout2.connect(r'33QY6790')
# 启动被测试的应用程序
device.app_start('com.yunyao.yuncc', wait=True)
# 清空应用数据
device.app_clear('com.yunyao.yuncc')
# 点击图床
device(resourceId="com.yunyao.yuncc:id/network_tuch").click()
#判断登录按钮是否存在
button = device(resourceId="com.yunyao.yuncc:id/go_login")
if button.exists():
# 登录
button.click()
device(resourceId="com.yunyao.yuncc:id/et_moble").send_keys('182116000000')
device(resourceId="com.yunyao.yuncc:id/et_password").send_keys('yunyao33607')
device(text='登录').click()
# 访问栏目
device(text='蛇年大吉').click()
# 退出应用程序
device.app_stop('com.yunyao.yuncc')
应用执行流程源码
# 导入库文件 重命为uiout2
import uiautomator2 as uiout2
# 编写对应函数
def test_logo_empty_mobile():
# 连接设备 (通过设备序列号,测试设备只有一台时序列号可以为空)
device = uiout2.connect(r'33QY6790')
# 启动被测试的应用程序
device.app_start('com.yunyao.yunllyu', wait=True)
# 进入登录页面
device(resourceId="com.yunyao.yunllyu:id/network_logo").click()
# 准备登录
device(resourceId="com.yunyao.yunllyu:id/fragment_logo_lemon_avatar_title").click()
# 输入用户手机号码
device(resourceId="com.yunyao.yuncc:id/et_moble").send_keys('')
# 输入用户密码
device(resourceId="com.yunyao.yuncc:id/et_password").send_keys('')
# 点击登录按钮
device(text='登录').click()
# 退出应用程序
device.app_stop()
# 断言 应用是否提示 手机号码或密码不能为空 测试结果在控制台输出
assert device.toast.get_message() == '手机号码或密码不能为空'
测试案例运行优化
1.pytest
库的安装
右键选择 pytest
运行测试代码
pip install pytest
2.逻辑代码的封装
# 导入库文件 重命为uiout2
import uiautomator2 as uiout2
# 导入库文件
import pytest
# 登录函数 传入3个参数:设备 用户名 密码
def logo(device, username, passwd):
device.app_start('com.yunyao.yunllyu', wait=True)
# 进入登录页面
device(resourceId="com.yunyao.yunllyu:id/network_logo").click()
# 准备登录
device(resourceId="com.yunyao.yunllyu:id/fragment_logo_lemon_avatar_title").click()
# 输入用户手机号码
device(resourceId="com.yunyao.yuncc:id/et_moble").send_keys(username)
# 输入用户密码
device(resourceId="com.yunyao.yuncc:id/et_password").send_keys(passwd)
# 点击登录按钮
device(text='登录').click()
# 退出应用程序
device.app_stop()
# 测试登录 函数
def test_logo_empty_mobile():
# 连接设备 (通过设备序列号,测试设备只有一台时序列号可以为空)
device = uiout2.connect(r'33QY6790')
# 启动被测试的应用程序
device.app_start('com.yunyao.yunllyu', wait=True)
# 调用登录函数 并传入参数
logo(device, '', '')
# 断言 应用是否提示 手机号码或密码不能为空 测试结果在控制台输出
assert device.toast.get_message() == '手机号码或密码不能为空'
# 使用 pytest 进行测试
if __name__ == '__main':
pytest.main()
3.mark
功能
给测试用例 打标签 在测试函数上面打标签
# @pytest.mark.标签名
@pytest.mark.logo_test
# 标签的使用
pytest.main(['-m logo_test'])
生成测试报告
1.安装库 pytest-html
pip install pytest-html
2.生成测试报告
# 测试报告文件名:testlogo.html 默认生成在当前文件夹
if __name__ == '__main':
pytest.main(['-m logo_test', '--html=testlogo.html'])
3.查看测试报告
测试报告默认生成在当前文件夹,选择测试报告 .html
文件用电脑浏览器打开