提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
macOS: 13.6.6
Xcode: 15.2
iPhone: 17.2.1
Appium: 2.5.4
安卓同事已经完成了谷歌等原生支付的自动化用例,领导让我解决iOS原生支付的用例。
一、实现的难点
当App内购时,会弹出苹果支付的支付按钮,但是这个按钮在appium-inspector中,是找不到元素的,因为苹果自身的安全考虑,苹果自己的XCUITest框架都inspect不到原生支付页面,而Appium是通过使用XCUITest driver来使用XCUITest框架实现自动化,更不可能找得到元素了。那么只能从其他角度考虑来实现原生支付了。
原生支付会有两大难点,第一个点击是“购买”按钮,另一个则是点击购买之后出现的输入密码框。
可喜的是,driver.tap([(x, y)], 1)的tap函数可以根据x、y坐标来点击原生支付页面,那么原生支付应该不难实现。
二、点击“购买”按钮
因为考虑到后续多个不同型号、分辨率的iOS设备都要跑用例,所以我的每一个坐标都不能写死,必须根据这个设备的特性来找到按钮,并点击。
比如如图这个“购买”按钮,如果我知道这个按钮的坐标,我用driver.tap()函数点击,就可以进入到下一步,那么这个坐标我要怎么获取呢?
我采取的方案是使用Appium的plugins插件中的images,也就是图片匹配查找元素坐标,如果我把“购买”二字截图出来,然后拿到App截图中去对比,就可以找到这个元素的坐标。
安装插件images
首先我们可以在控制台执行命令:
appium plugin list --installed
查看当前是否安装了插件,我的结果是:
eibyshen@Eibys-MBP ~ % appium plugin list --installed
✔ Listing installed plugins
- execute-driver@3.0.28 [installed (npm)]
- images@3.0.6 [installed (npm)]
如果没有显示这个images查看的,可以执行命令进行安装:
appium plugin install images
然后启动Appium时,命令需指定使用plugins,命令从:
appium
改为:
appium --use-plugins=images
但是我当时执行的时候,并没有生效,images插件仍旧无法使用。因为我的电脑是intel的,所以还存在需要查看opencv是否安装成功的问题。
intel电脑安装opencv
1、安装opencv
brew install opencv
2、查看还差什么依赖
brew info opencv
然后我补了以下三个依赖
brew install pkg-config
brew install cmake
brew install python-setuptools
最后修改了配置文件
open /Users/{YOUR_NAME}/.zprofile
export PKG_CONFIG_PATH=“/usr/local/opt/opencv/lib/pkgconfig”
export LDFLAGS=“-L/usr/local/opt/opencv/lib”
export CPPFLAGS=“-I/usr/local/opt/opencv/include”
如果你安装插件images之后没有遇到问题,那么启动Appium应该可以看到images后面会显示“active”,说明已经启用了:
python代码实现
准备图片
首先需要准备screenshot函数的截图,因为图片识别对图片大小很敏感,在执行用例的时候,我会使用driver.save_screenshot()函数的截图作为原始图,然后从这个截图裁剪“购买”按钮作为partial picture,局部图片。注意我这里是用的“裁剪”,不是局部截图,再次强调图片识别对图片大小很敏感。我的购买按钮裁剪出来是这样:
这个购买图片与截图中购买按钮大小应一致。这个图片的全路径,我们保存到变量partial_pic_path,然后执行用例时,到支付页面时,我们driver.save_screenshot()一个截图,全路径保存到变量whole_pic_path,实现代码为:
def is_part_in_picture(driver, partial_pic_path, whole_pic_path):
# 判断是否存在图片,不存在则返回false,try except
driver.update_settings({"fixImageTemplatescale": True})
try:
position = driver.find_image_occurrence(base64_partial_image=load_image_base64(partial_pic_path), base64_full_image=load_image_base64(whole_pic_path), visualize=True)
except:
pass
return position
然后load_image_base64()函数为(抄的别人的,忘了出处了,抱歉):
def load_image_base64(path: str):
with open(path, "rb") as f:
return base64.b64encode(f.read()).decode("UTF-8")
以上position返回的格式为:
{"rect":{"x":518,"y":2307,"width":141,"height":78},"score":1,"visualization":null,"multiple":[{"score":1,"rect":{"x":518,"y":2307,"width":141,"height":78}}]}
其中x和y是图片的像素,所以不能直接当成页面坐标进行点击。还需要找到截图的图片的宽高,和App页面的宽高计算出按钮的x、y坐标。
找到App的宽和高
对于有刘海的iOS设备,当调用driver.get_window_size()函数的时候,实际上返回的宽高是安全区域内的宽高,英文叫SafeArea。如果元素放置在安全区域外,可能元素会显示不完整,被刘海挡住,所以就定义了一个安全区域出来。
所以我现在需要找到的App宽高,用driver.get_window_size()没有用。于是我考虑用每个页面的老大来获取整个页面的宽高:
然后截图的宽高,可以使用pillow获取:
from PIL import Image
image = Image.open(whole_pic_path)
width, height = image.size
当然在此之前需要安装pillow
pip install pillow
然后根据比值计算出购买按钮截图的中间的x、y坐标。
图片匹配的输出流
driver.find_image_occurrence()函数中有一个参数是visualize,如果设置为True的话,会返回一个图片流,显示图片查找的结果。结果放在返回值中的visualization里,然后转换成png图片保存到本地就可以查看到效果:
import base64
from PIL import Image
from io import BytesIO
def output_base64_image(base64_pic):
image_data = base64.b64decode(base64_pic)
image = Image.open(BytesIO(image_data))
image.save("output.png", "PNG")
以下是效果图,我匹配的输入框的样式:
后话
由于不同设备的屏幕大小、分辨率不同,我的这个购买按钮图片可能会有点不匹配对应设备的截图,这个时候,就需要调整图片识别里的参数,比如不需要绝对匹配,可以模糊一点等,这个等到后面再深入研究。
三、密码输入框
当我实现了图片匹配找到坐标之后我就飘了,觉得那么迷茫输入框也很简单了呀,直接把密码图片拿去匹配啊,直到我用inspector查看密码输入页面时,页面时这样的:
真实设备这个密码输入框区域是有可点击的密码的,但是inspector里啥都没有。然后从inspector里查看元素,其实是看得到键盘元素的,好吧,偶尔看得到,经过反复代码验证,是操作不了的,怎么办呢?
我的解决方案是到一个可以输入文字的页面去,获取键盘上按钮的坐标:
然后返回到输入密码的地方,一个坐标一个坐标敲密码,就可以实现了。
至此,苹果原生支付的用例就实现了。
四、总结
第二步中图片识别的用途其实还有很多,不一定非要用来找按钮,比如后面我们公司会有测试用例需判断歌词走字是按照一句一句走还是一个字一个字走,在无法靠找元素实现的时候,可能可以用图片识别来解决问题。
参考资料
https://blog.csdn.net/m0_67695717/article/details/135002667
https://blog.csdn.net/qq_52804277/article/details/136588759