Android 自动化测试
Python +Android +uiautomator test 在init中定义的方法
uiautomator
该模块是android的一个python包装uiautomator测试框架。它适用于Android 4.1+,只需通过adb连接Android设备,无需在Android设备上安装任何东西。
从 uiautomator 进口设备的 ð
d.screen.on()
D(id= “时钟”)。点击()
安装
$ pip install uiautomator
前提条件
- 安装Android的SDK,并设置
ANDROID_HOME
环境以正确的路径。 - 启用设备上的ADB设置,并使用USB连接您的Android设备与您的电脑。
导入uiautomator
-
如果
ANDROID_SERIAL
在环境中定义的,或只有一个连接的设备:从 uiautomator 进口设备的 ð
-
检索设备对象时,限制序列号
从 uiautomator 进口设备 ð =设备(' 014E05DE0F02000E “)
-
speficy在其他计算机上运行的adb服务器主机和端口
虽然亚行支持
-a
选项SDK 4.3以来,但现在它有一个bug。在所有接口而不是localhost上启动adb服务器侦听的唯一方法是adb -a -P 5037 fork-server server &
从 uiautomator 进口设备 ð =设备(' 014E05DE0F02000E ',adb_server_host = “ 192.168.1.68 ”,adb_server_port = 5037)
注:在下面的例子中,我们使用d
代表Android设备对象。
目录
基本API用法
此部分通过一些简单的示例显示设备的正常操作。
-
检索设备信息
d.info
以下是可能的结果:
{ u'displayRotation': 0, u'displaySizeDpY': 640, u'displaySizeDpX': 360, u'currentPackageName': u'com.android.launcher', u'productName': u'takju', u'displayWidth': 720, u'sdkInt': 18, u'displayHeight': 1184, u'naturalOrientation': True }
关键事件设备的操作
-
打开/关闭屏幕
#在屏幕上打开 d.screen.on() #关闭屏幕 d.screen.off()
替代方法是:
#唤醒设备 d.wakeup() #睡眠设备,一样关闭屏幕。 d.sleep()
-
按硬/软键
#按home键 d.press.home() #按返回键 d.press.back() #正常的方式按返回键 d.press( “回”) #按下键码0×07(0)与元ALT (0X02)上 d.press( 0x的 07, 0X 02)
-
目前支持下列键:
home
back
left
right
up
down
center
menu
search
enter
delete
(或del
)recent
(最近的应用程式)volume_up
volume_down
volume_mute
camera
power
你可以找到所有关键代码定义的Android的KeyEvent。
手势设备的交互
-
单击屏幕
#点击(X,Y)在屏幕上 d.click(X,Y)
-
长按屏幕
#长按(X,Y)在屏幕上 d.long_click(X,Y)
-
滑动
#从(SX,SY)轻扫(EX,EY) d.swipe(SX,SY,EX,EY) #从(SX,SY)轻扫(EX,EY)与10个步骤 d.swipe(SX,SY ,前,安永,步骤= 10)
-
拖动
#从(SX,SY)拖动(EX,EY) d.drag(SX,SY,EX,EY) #从(SX,SY)拖动(EX,EY)与10个步骤 d.drag(SX,SY ,前,安永,步骤= 10)
屏幕设备的操作
-
检索/设置方向
可能的方向是:
natural
要么n
left
要么l
right
要么r
upsidedown
或u
(不能设定)
#检索方向,也可能是“天然”或“左”或“右”或“upsidedown” 方向 = d.orientation #设置定向和冻结旋转。 #指出:“upsidedown”不能设置到Android 4.3的。 d.orientation = “升” #或“左”的 d.orientation = “ - [R ” #或“右” d.orientation = “ ñ ” #或“自然”
-
冻结/取消冻结旋转
#冻结旋转 d.freeze_rotation() #取消冻结旋转 d.freeze_rotation(假)
-
截取屏幕截图
#采取截图并保存到本地文件“home.png”,直到Android 4.2或不能工作。 d.screenshot( “ home.png ”)
-
转储窗口层次结构
#转储widown层次结构并保存到本地文件“hierarchy.xml” d.dump( “ hierarchy.xml ”) #或获得回报倾倒内容(Unicode)的。 XML = d.dump()
-
打开通知或快速设置
#开放的通知,不能工作,直到Android 4.3的。 d.open.notification() #开启快速设定,不能工作,直到Android 4.3的。 d.open.quick_settings()
-
等待空闲或窗口更新
#等待当前窗口空闲 d.wait.idle() #等待,直到窗口更新事件发生 d.wait.update()
观察者
你可以注册守望者执行某些动作时,选择器不能找到匹配。
-
注册观察者
当选择器找不到匹配项时,uiautomator将运行所有注册的观察器。
- 条件匹配时点击目标
d.watcher( “ AUTO_FC_WHEN_ANR ”)。当(文= “ ANR ”)。当(文= “等待”)\ 。点击(文字= “强制关闭”) # d.watcher(名)##创建一个新的命名观察者。 # 。当(条件)##观察者的UiSelector条件。 # 。点击(目标)##执行对目标UiSelector点击动作。
- 条件匹配时按键
d.watcher( “ AUTO_FC_WHEN_ANR ”)。当(文= “ ANR ”)。当(文= “等待”)\ .press.back.home() #另类的方式来定义它,如下 d.watcher( “ AUTO_FC_WHEN_ANR ”)。当(文= “ ANR ”)。当(文= “等待”)\ 。按(“回”,“家”) # d.watcher(名)##创建一个新的名为守望。# 。当(条件)##观察者的UiSelector条件。# 。按<键名> ...... <键名>。()##按下按键逐个顺序。# Alternavie方式定义按键顺序是按(<keybname>,...,<键名>)
-
检查命名的监视器是否触发
触发观察者,这意味着观察者运行并且其所有条件都匹配。
d.watcher( “ watcher_name ”).triggered #真在指定守望的情况下触发,否则为假
-
删除命名监视器
#删除观察者 d.watcher( “ watcher_name ”)上卸下摆臂()
-
列出所有观察者
d.watchers #所有注册wachers'名称的列表
-
检查是否有任何观察者触发
d.watchers.triggered # 真正的触发任何看守的情况下,
-
重置所有触发的观察者
#重置所有触发观察家,在那之后,d.watchers.triggered会是假的。 d.watchers.reset()
-
Remvoe观察者
#删除所有已注册的观察家 d.watchers.remove() #删除指定的守望者一样,同样d.watcher(“watcher_name”)。remove()方法 d.watchers.remove( “ watcher_name ”)
-
强制运行所有观察者
#力量来运行所有注册的观察者 d.watchers.run()
处理程序
处理程序的功能与Watcher相同,只是它实现了我们的Android uiautomator。处理程序和观察程序之间最不同的用法是,处理程序可以使用自定义的回调函数。
高清 fc_close(设备):
如果设备(文= '强制关闭“).exists:
设备(文= '强制关闭”)。点击()
返回 真 #返回True手段打破处理程序回调函数的循环。
#在处理回调函数反过来
d.handlers.on(fc_close) #关闭句柄回调函数
d.handlers.off(fc_close)
选择器
选择器是标识当前窗口中的特定ui对象。
#要入围对象,文字是“时钟”和它的类名是“android.widget.TextView'
D(文= '时钟',的className = ' android.widget.TextView ')
选择器支持以下参数。请参阅UiSelector DOC java的详细信息。
text
,textContains
,textMatches
,textStartsWith
className
,classNameMatches
description
,descriptionContains
,descriptionMatches
,descriptionStartsWith
checkable
,checked
,clickable
,longClickable
scrollable
,enabled
,focusable
,focused
,selected
packageName
,packageNameMatches
resourceId
,resourceIdMatches
index
,instance
子对象和同级UI对象
-
儿童
#得到孩子或孙子 D(的className = “ android.widget.ListView ”).child(文= “蓝牙”)
-
兄弟
#得到同胞的兄弟姐妹或子女 D(文= “谷歌”).sibling(的className = “ android.widget.ImageView ”)
-
子文本或描述或实例
#得到孩子匹配的className =“android.widget.LinearLayout” #而且它或它的子女或孙子包含文本“蓝牙” D(的className = “ android.widget.ListView ”, RESOURCEID = “机器人:ID /列表”) \ .child_by_text( “蓝牙”,的className = “ android.widget.LinearLayout ”)#允许滚动搜索,获得子 D(的className = “ android.widget.ListView ”, RESOURCEID = “机器人:ID /列表”)\ 。 child_by_text( “蓝牙”, allow_scroll_search = 真, 的className = “ android.widget.LinearLayout ” )
-
child_by_description
是找到子哪个或哪些是孙子包含指定的描述中,其他是相同的child_by_text
。 -
child_by_instance
是在其子层次结构中指定实例的任何位置找到具有子UI元素的子项。这是一个没有可见的意见进行滚动。
详情请参阅以下链接:
- UiScrollable,
getChildByDescription
,getChildByText
,getChildByInstance
- UiCollection,
getChildByDescription
,getChildByText
,getChildByInstance
上面的方法支持链接调用,例如对于下层
< 节点 指数 = “ 0 ” 文本 = “ ” 资源ID = “机器人:ID /列表” 级 = “ android.widget.ListView ” ...> < 节点 指数 = “ 0 ” 文本 = “ WIRELESS & NETWORKS ” 资源-id = “ ” 类 = “ android.widget.TextView ” ... /> < 节点 索引 = “ 1 ” 文本 = “ ” 资源ID = “ ” 类 = “ android.widget.LinearLayout ” ...> < 节点 指数 = “ 1 ” 文本 = “ ” 资源ID = “ ” 级 = “ android.widget.RelativeLayout ” ...> < 节点 指数 = “ 0 ” 文本 = “无线网络连接” 资源ID = “机器人: ID /标题“ 级 = ” android.widget.TextView “ ... /> </ 节点 > < 节点 指数 = ” 2 “ 文本 = ” ON “ 资源ID = ” com.android.settings:ID / switchWidget “ 类 = “ android.widget.Switch ” ... /> </ 节点 > ... </ 节点 >
我们要点击文本“Wi-Fi”右侧的开关打开/打开Wi-Fi。因为有几个开关,几乎相同的属性,所以我们不能使用类似
d(className="android.widget.Switch")
选择的UI对象。相反,我们可以使用下面的代码来选择它。D(的className = “ android.widget.ListView ”,RESOURCEID = “机器人:ID /列表”)\ .child_by_text( “无线网络连接”,的className = “ android.widget.LinearLayout ”)\ .child(的className = “机器人.widget.Switch “)\ 。点击()
-
-
相对位置
此外,我们可以用相对位置的方法来获取视图:
left
,right
,top
,bottom
。d(A).left(B)
,意味着在左侧选择B。d(A).right(B)
,表示选择A右侧的B.d(A).up(B)
,表示选择B以上的A.d(A).down(B)
,表示在A下选择B.
所以对于上面的情况,我们可以写代码:
##选择的“无线网络连接”右侧的“开关” D(文= “无线网络连接”).right(的className = “ android.widget.Switch ”)。点击()
-
多个实例
有时,屏幕可能包含多个视图与相同的例如文本,那么你将不得不使用选择器中的“实例”属性,如下所示:
D(文= “新增”,比如= 0) #这意味着一审文本“新增”
但是,uiautomator提供了类似的方法来使用它。
#得到的文本意见的计当前屏幕上的“添加新的” D(文= “新增”).Count之间#一样count属性LEN(D(文= “新增”)) #通过索引获取实例 ð (文= “新增”)[ 0 ] D(文= “新增”)[ 1 ] ... #迭代器的视图中 D(文= “添加新”): view.info # ...
注意:当您使用选择喜欢的列表,你必须确保屏幕保持不变,否则你可能会得到UI未找到错误。
获取所选的ui对象状态及其信息
-
检查特定ui对象是否存在
D(文= “设置”).exists #是否存在真,否则假 d.exists(文= “设置”)#以上财产的别名。
-
检索特定ui对象的信息
D(文= “设置”).INFO
以下是可能的结果:
{ u'contentDescription': u'', u'checked': False, u'scrollable': False, u'text': u'Settings', u'packageName': u'com.android.launcher', u'selected': False, u'enabled': True, u'bounds': {u'top': 385, u'right': 360, u'bottom': 585, u'left': 200}, u'className': u'android.widget.TextView', u'focused': False, u'focusable': True, u'clickable': True, u'chileCount': 0, u'longClickable': True, u'visibleBounds': {u'top': 385, u'right': 360, u'bottom': 585, u'left': 200}, u'checkable': False }
-
设置/清除可编辑字段的文本
D(文= “设置”).clear_text() #清除文字 D(文= “设置”).set_text( “我的文字...... ”) #设置文本
对选中的ui对象执行单击操作
-
点击特定的ui对象
#点击特定的UI对象的中心 D(文= “设置”)。单击() #点击具体的UI对象的bottomright角落 D(文= “设置”)).click.bottomright( #请点击具体的UI对象的左上边角 D(文= “设置”).click.topleft() #点击等到新窗口更新 D(文= “设置”).click.wait()
-
长时间点击特定的ui对象
#长按一下特定的UI对象的中心 D(文= “设置”).long_click() #长按具体的UI对象的bottomright角落 D(文= “设置”).long_click.bottomright() #长按具体的UI对象的左上边角 D(文= “设置”).long_click.topleft()
针对特定ui对象的手势动作
-
将ui对象拖动到另一个点或ui对象
#注:拖不能设置到Android 4.3的。 #拖拽的UI对象,以点(X,Y) D(文= “设置”).drag.to(X,Y,步骤= 100) #拖拽的UI对象到另一个UI对象(中心), D(文= “设置“).drag.to(文= ”时钟“,步骤= 50)
-
从ui对象的中央滑动到其边缘
滑动支持4个方向:
left
right
top
bottom
D(文= “设置”).swipe.right() D(文= “设置”).swipe.left(步骤= 10), D(文= “设置”).swipe.up(步骤= 10), D(文字= “设置”).swipe.down()
-
两点手势从一个点到另一个点
D(文= “设置”).gesture((SX1,SY1),(SX2,SY2))\ 。要((EX1,EY1),(EX2,EY2))
-
在特定ui对象的两点姿态
支持两种手势:
In
,从边到中心Out
,从中心到边缘
#注:捏不能设置到Android 4.3的。 #从边缘到中心。这里是“在”不“,在” D(文= “设置”).pinch.In(百分比= 100,步= 10) #从中心到边缘 D(文= “设置”).pinch.Out()
-
等待特定ui对象出现或消失
#等到UI对象出现 D(文= “设置”).wait.exists(超时= 3000) #等待,直到UI对象转眼 D(文= “设置”).wait.gone(超时= 1000)
-
对特定的ui对象执行fling(可滚动)
可能的属性:
horiz
要么vert
forward
或backward
或toBeginning
或toEnd
#一扔前进(默认)垂直(默认) D(滚动= 真).fling() #一扔前进horizentally D(滚动= 真).fling.horiz.forward() #一扔向后垂直 D(滚动= 真).fling .vert.backward() #一扔到horizentally开始 D(滚动= 真).fling.horiz.toBeginning( max_swipes = 1000) #来结束垂直一扔 D(滚动= 真).fling.toEnd()
-
在特定的ui对象上滚动(可滚动)
可能的属性:
horiz
要么vert
forward
或backward
或toBeginning
或toEnd
或to
#向前滚动(默认)垂直(默认) D(滚动= 真).scroll(步骤= 10) #向前滚动horizentally D(滚动= 真).scroll.horiz.forward(步骤= 100) #向后滚动垂直 D(滚动= 真).scroll.vert.backward() #滚动到horizentally开始 D(滚动= 真).scroll.horiz.toBeginning(步骤= 100, max_swipes = 1000) #滚动到年底垂直 D(滚动= 真)。 scroll.toEnd() #向前滚动,直到垂直特定的UI对象出现 D(滚动= 真).scroll.to(文= “安全性”)
贡献
- 分叉repo,并克隆到您的计算机。
- 从检出一个新分支
develop
的分支 - 安装要求:
pip install -r requirements.txt
- 进行更改,然后更新测试。不要忘记在“提供者”部分的末尾添加您的姓名
- 通过所有的测试和代码必须包括:
tox
。 - 提交你的修改,并提交拉请求
develop
分支。
贡献者
问题和讨论
如果您有任何错误报告或烦恼请报告给我们的问题跟踪GitHub的问题。
如果你有你想讨论任何建议,新的功能要求或话题,请提交您的主题ostio。
笔记
- Android的uiautomator适用于Android 4.1及以上版本,所以在使用它之前,请确保你的设备是Android4.1 +。
- 有些方法仅工作在Android 4.2 / 4.3,所以你最好阅读详细uiautomator的Java文档在使用它之前。
- 该模块采用uiautomator-jsonrpc服务器作为后台程序与设备进行通信。
- 该模块仅在python2.7 / 3.2 / 3.3 / pypy上测试。
常问问题
-
无法启动JSONRPC服务器:
raise IOError("RPC server not started!")
它可能是由网络,设备或环境引起的。因此,当您遇到此问题,请按照以下步骤,尝试手动启动JSONRPC服务器。
-
从下载jar文件uiautomator jsonrpc服务器。
-
Adb将下载的jar文件推送到
/data/local/tmp/
-
通过命令启动jsonrpc服务器:
adb shell uiautomator runtest bundle.jar uiautomator-stub.jar -c com.github.uiautomatorstub.Stub
-
Adb将本地端口转发到设备端口:
adb forward tcp:9008 tcp:9008
-
检查jsonrpc服务器是否正常:
curl -d '{"jsonrpc":"2.0","method":"deviceInfo","id":1}' localhost:9008/jsonrpc/0
如果你看到类似的消息
{"jsonrpc":"2.0","id":1,"result":{"currentPackageName":"android","displayHeight":1280,"displayRotation":0,"displaySizeDpX":0,"displaySizeDpY":0,"displayWidth":720,"productName":"falcon","sdkInt":17,"naturalOrientation":true}}
,则表示服务器已启动。
如果你可以手动启动jsonrpc服务器,但你的脚本总是满足
IOError("RPC server not started!")
,请提交问题github上的问题。 -
-
错误
httplib.BadStatusLine: ''
JsonRPC服务器需要访问设备上的临时目录,但在一些低层设备上,它可能会遇到错误,在访问临时文件没有连接SD卡。因此,如果您遇到错误,请插入SD卡,然后重试。