Poco元素定位和脚本编写顺序

上期回顾:Airtest局部截图+找图、截屏另存为


以下基于
python3.8;airtestIDE1.2.13;airtest1.2.4;pocoui1.0.85

Airtest框架讲的差不多了,本期开始讲Poco框架,注意:Poco框架和Airtest框架很多API是同名的,但使用方法完全不一样!!!一定不要搞混了,我初学时也经常搞混,这点一定要注意!
具体Poco框架和Airtest框架是什么关系,可以看之前文章:Airtest Project——UI自动化利器介绍

今天的主题是Poco框架的元素定位,在讲之前我们先普及一个基础知识,就是Poco框架脚本内容的编写顺序,新手经常会掉这个坑里。

Poco脚本编写顺序

在讲之前不熟悉Poco的可以先看下历史文章:
AirtestIDE实践二:Poco框架试用
Airtest之使用Poco测试Android原生应用
Airtest之使用Poco测试iOS原生应用

正确的Poco脚本编写顺序应该是:连接设备-->打开应用&等待应用启动完成-->初始化poco
新手不按这个顺序的话,有可能出现各种异常报错。
纯代码连接设备方法可以看之前的Airtest API精讲之设备连接管理API集合

示例:

# -*- encoding=utf8 -*-
__author__ = "测试工程师小站"

from airtest.core.api import *
from poco.drivers.android.uiautomation import AndroidUiautomationPoco

# 连接设备、初始化日志路径
auto_setup(__file__, logdir=True, devices=["Android:///"])

# 启动计算器
start_app("com.miui.calculator")
sleep(3)

# 初始化安卓原生poco
poco = AndroidUiautomationPoco(use_airtest_input=True, screenshot_each_action=False)

# 依次点1+1=,这块代码可以使用IDE左下的录制功能
poco("com.miui.calculator:id/digit_1").click()
poco("com.miui.calculator:id/op_add").click()
poco("com.miui.calculator:id/digit_1").click()
poco("com.miui.calculator:id/btn_equal_s").click()

# 获取结果控件的文本并断言,可以使用IDE左下的锁定功能,并找到结果控件
r = poco("com.miui.calculator:id/result").get_text()
assert_equal("= 2", r, "结果=2")

Poco元素定位

Poco元素定位需要用到AirtestIDE中的'Poco辅助窗'功能,之前有介绍AirtestIDE基本功能(一)
AirtestIDE的录制功能可自动生成元素定位代码,但和所有自动化工具的录制功能一样,现在的工具还没有那么智能,录制出的定位代码可能无法定位、代码太长太冗余、兼容性不好(下个版本UI有一点小改动就导致定位失效)。所以录制可以辅助,但一定还要熟练掌握定位基础和技巧。

Poco官方将元素定位分为了:基本选择器、相对选择器、空间选择器,以及通过正则表达式匹配。熟悉Selenium或Appium的朋友可以无缝上手。

基本选择器

我们对元素定位的目标就是唯一,即使下个版本有一些UI改动,也能尽量不影响之前的定位代码。像Selenium、Appium,如果开发配合,应该给所有元素一个唯一的id值,我们通过id即可唯一定位一个元素。在Poco中,'name'属性就是类似于Selenium的'id'属性的存在。

我们以安卓设置为例

图片

一个经典的定位:

poco(name="android:id/title"text="语言与输入法")

# 在poco中,如果不写属性名,则默认该属性为name,所以也可以写成
poco("android:id/title"text="语言与输入法")

# 在这个例子中,设置中的所有文字选项的name都是"android:id/title"
# 但"text"属性是唯一的,所以可以写成更简单的
poco(text="语言与输入法")

上面就是一个简单的定位的例子,一般情况下,如果开发配合,给"name"属性都设为全局唯一名称,那我们的定位工作将会非常简单。如果受公司所限开发无法配合,一般"text"属性也是唯一的。
我们定位的原则就是用最少的属性全局唯一定位一个元素,优先用"name"、"text"单属性定位,如果单属性定位不唯一,就组合起来使用,仍不唯一时,可以继续加入其他属性,如"type"、"touchable"、"clickable"、"visible"等。

空间选择器

空间选择器就是有一组元素时,利用索引下标来确定唯一元素,下标是从0开始。
下来我们以Airtest官网提供的Unity的App为例

图片

 

图片

 

# 我们的目标是点击鲨鱼图片元素

# 鲨鱼是第1个'Image',即下标0
poco("Image")[0].click()


# 也可以和相对选择器组合使用
node = poco("fish")[0]  # 先定位到鲨鱼的整个节点
# 上面那行也可以拆分写成2行
# node_list = poco("fish")
# node = node_list[0]

shark = node.child("Image")  # 再定位到其下的"Image"元素
shark.click()

但使用空间选择器需要注意,有时我们App内的元素索引是会变的,比如上面例子中第1个(索引0)是鲨鱼,假如我们点击后,又多出来一个鲤鱼,并且排在第1位,这时候鲨鱼就变第2个了,所以还想再次点击鲨鱼的话,需要重新定位一次

poco("Image")[1].click()

相对选择器

有时候,通过基本选择器、空间选择器无法定位到元素时,可以使用相对选择器来定位。
上面我们已经例举了一个相对选择器的例子,即先定位到鲨鱼的整个节点元素'fish',再定位他的子元素'Image'
Poco有如下相对选择器:

  • child(name=None, **attrs)
    返回第1个符合条件的子元素

  • children()
    返回所有子元素

  • offspring(name=None, **attrs)
    返回符合条件的子孙元素

  • parent()
    返回父元素

  • sibling(name=None, **attrs)
    返回符合条件的兄弟元素

还是以此图为例:

图片

 

# 返回playLocalPositioning的第1个子元素,即鲨鱼的node元素'fish'
shark_node = poco("playLocalPositioning").child()

# 返回鲨鱼的子元素name
shark_name = shark_node.child("name")
print(shark_name.get_text())

# 返回playLocalPositioning的所有子元素
fish_list = poco("playLocalPositioning").children()
shark_node = fish_list[0]

# 返回playLocalPositioning的所有符合条件的子孙元素(下一层)
fish_list = poco("playLocalPositioning").offspring("fish")
shark_node = fish_list[0]

# 返回playLocalPositioning的所有符合条件的子孙元素(下下一层)
fish_list = poco("playLocalPositioning").offspring("name")
shark_name = fish_list[0]
print(shark_name.get_text())

# 返回Canvas的所有符合条件的子孙元素(下下下下一层)
shark_name = poco("Canvas").offspring("name")

# 返回鲨鱼文字的父节点
shark_node = poco(text="shark").parent()

# 返回鲨鱼文字的兄弟节点图片
shark_img = poco(text="shark").sibling("Image")

# 返回鲨鱼node节点的兄弟节点
# 按理说应该是3个fish节点,但poco会加上其父节点playLocalPositioning,不知道是不是bug
fish_list = poco(text="shark").parent().sibling()
# 所以我们限定一下只要3个fish节点
fish_list = poco(text="shark").parent().sibling("fish")

# 通过鲨鱼文字元素定位珍珠文字元素
pearl_node = poco(text="shark").parent().sibling("fish")[2]
pearl_name = pearl_node.child("name")
print(pearl_name.get_text())

# 打印所有图片的名字文字
fish_list = poco("fish")
for fish in fish_list:
    print(fish.child("name").get_text())

通过正则表达式定位元素

Poco元素的各个属性,都是字符串,所以正则定位元素和Python中的字符串正则匹配是一样的。不会正则的可以先去学Python正则。

我们以第1个例子安卓配置为例,我们要定位"语言与输入法",但是不同的MIUI版本或是三星、华为手机该项名称可能是不一样的,比如:
MIUI10该元素的text="语言与输入法"
MIUI11该元素的text="语言和输入法"
三星该元素的text="输入法"
华为该元素的text="输入法设置"

为了同时兼容4台手机,我们可以通过if来判断手机型号然后定位元素,很复杂而且要写很多代码,这时就可以使用正则定位元素

# 一行代码同时兼容4台手机
poco(textMatches=".*输入法.*")

或者有时name属性是类似的,我们想定位所有类似元素。如有3个元素name分别是"color_red"、"color_green"、"color_yellow"、

# 返回3个颜色元素的一个list
color_list = poco(nameMatches="color_.*")

除了最常见的textMatches、nameMatches和typeMatches,其实大部分的属性都可以用这种方式来传递正则表达式,只要能够用 poco(xx=预期属性值) 来选择的控件,就可以用 poco(xxMatches=预期属性值的正则表达式) 来进行匹配定位。

 

---------------------------------------------------------------------------------

关注微信公众号即可在手机上查阅,并可接收更多测试分享~

posted @   ☆星空物语☆  阅读(1722)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示