1、什么是Hypium
Hypium是华为官方为鸿蒙操作系统开发的一款以python为语言的自动化测试框架。
引用华为官网介绍如下:
DevEco Testing Hypium(以下简称Hypium)是HarmonyOS平台的UI自动化测试框架,支持开发者使用python语言为应用编写UI自动化测试脚本,主要包含以下特性:
- Hypium提供了原生控件/图像/比例坐标等多种控件定位能力,支持多窗口操作以及触摸屏/鼠标/键盘等多种模拟输入功能,支持多设备并行操作,能够覆盖各类场景和多种形态设备上的自动化用例编写需求。
- Hypium包含配套用例编写辅助插件, 支持控件查看/投屏操作等多种用例开发辅助功能,提升用例开发体验和效率。
- Hypium能够为执行的用例生成详细的用例执行报告,并且自动记录设备日志以及执行步骤截图,为开发者和测试人员提供高效和专业的测试用例执行和结果分析体验。
2、环境安装
1、python环境
官网推荐使用python3.10版本
2、下载安装hdc
类似于Android的adb工具包,并配置环境变量
配置鸿蒙HDC环境(等价于 android 端的 adb)
下载 Command Line Tools 并解压
hdc文件在command-line-tools/sdk/HarmonyOS-NEXT-DB2/openharmony/toolchains目录下
配置环境变量,macOS 为例,在~/.bash_profile 或者 ~/.zshrc 文件中添加
export HM_SDK_HOME="/Users/develop/command-line-tools/sdk/HarmonyOS-NEXT-DB2" //请以sdk实际安装目录为准
export PATH=$PATH:$HM_SDK_HOME/hms/toolchains:$HM_SDK_HOME/openharmony/toolchains
export HDC_SERVER_PORT=7035
详情请参考DevEco Studio使用指南及调试工具-hdc
手机端也要先开启开发者选项,在设置--关于本机---软件版本,连点到出现开发者选项打开
然后到设置--系统--开发者选项--打开usb调试开关,并信任
配置完成可以在终端执行,hdc list targets 命令,如果能返回手机设备序列号,说明成功
3、Hypium安装
去
下载中心 | 华为开发者联盟-HarmonyOS开发者官网,共建鸿蒙生态,下载
在页面最底部下载Hypium安装包,解压后找到其中的其中的hypium-5.0.7.200.zip(请以实际版本号为准)。
解压后该文件后得到的4个tar.gz格式的pip安装包,使用pip install命令安装。
Hypium安装对xdevice有依赖,优先安装xdevice,以下版本号仅做示例,请以实际版本号为准。
pip install xdevice-5.0.7.200.tar.gz
pip install xdevice-devicetest-5.0.7.200.tar.gz
pip install xdevice-ohos-5.0.7.200.tar.gz
pip install hypium-5.0.7.200.tar.gz
pycharm安装hypium插件
如果使用的是pycharm高版本,需要去Other Versions - PyCharm
下载旧版本pycharm,新版还不兼容,已提交问题给官方,官方建议下载旧版本hypium,下载了一个不行。需要降低pycharm版本,当前使用的是2024-2版本
安装后重启pycahrm即可
4、创建工程
pycharm新建工程选择hypium插件,填写工程名称和位置即可创建新工程
3、框架了解
1、工程目录介绍
2、配置文件介绍
<?xml version="1.0" encoding="UTF-8"?>
<user_config><environment><!-- type: 设备连接方式,usb-hdc表示使用hdc命令控制设备(默认) --><device type="usb-hdc"><!-- ip: 远端设备地址,ip和port为空时使用本地设备,非空时使用远端设备 --><ip></ip> <!-- port: 远端设备端口号 --><port></port> <!-- sn: 设备SN号列表,SN之间用分号";"分隔,sn字段为空时使用所有本地设备,非空时使用指定的sn设备 --><sn></sn> </device></environment><testcases><!-- 指定测试用例目录,为空则默认设置为当前项目下的testcase文件夹 --><dir></dir></testcases><resource><!-- 指定资源目录,为空则默认设置为当前项目下的resource文件夹 --><dir></dir></resource><!-- 默认为INFO,如需更详细信息可设置为DEBUG --><loglevel>DEBUG</loglevel><devicelog><!--在测试用例结束后额外后拉取以下路径的日志到报告下--><dir>/data/log/tee;/data/log/test</dir><!--控制hilog日志等级,默认值为INFO--><loglevel>DEBUG</loglevel> <!--控制是否在拉取日志后设备端的日志,默认值为true--><clear></clear> <!--控制是否抓取设备日志,默认值为ON,OFF时候上述两个标签不生效--><enable>ON</enable> </devicelog>
</user_config>
3、测试用例介绍
Hypium测试用例由两部分组成,分别是测试用例配置文件以及测试用例文件。其中测试用例的形态分两种模式,单个用例模式(一个测试用例py文件,一个测试配置json文件)以及测试套模式(一个测试套py文件、N个测试用例py文件、一个测试配置json文件),开发者可以根据业务的需要自行选择开发模式。
两种模式可以理解为,单条用例执行,和多条用例执行。
测试用例的生命周期函数主要有三个,分别是setup、process、teardown
# !/usr/bin/env python
# coding: utf-8
"""
#!!================================================================
#版权 (C) 2023, Huawei Technologies Co.
#==================================================================
#文 件 名: Example.py
#文件说明: Example TestScript
#作 者: author
#生成日期: 2023-07-13
#!!================================================================
"""from devicetest.core.test_case import TestCase, Step
from hypium import *class Example(TestCase):def __init__(self, controllers):self.TAG = self.__class__.__name__TestCase.__init__(self, self.TAG, controllers)self.driver = UiDriver(self.device1)def setup(self):Step('1.回到桌面')self.driver.swipe_to_home()def process(self):Step('2.启动设置应用')self.driver.start_app(package_name="com.huawei.hmos.settings", page_name="com.huawei.hmos.settings.MainAbility")def teardown(self):Step('3.关闭设置应用')self.driver.stop_app("com.huawei.hmos.settings")
测试用例配置文件介绍
{"description": "Config for OpenHarmony app test suites",//environment字段主要描述测试用例需要的环境信息,如需要多少个设备"environment": [{"type": "device", //device表示OpenHarmony设备"label": "phone" //设备类型,phone为手机,tablet为平板,默认不填写则对设备无要求},{"type": "device", //多个设备时填写"label": "phone"}],// driver字段主要描述测试用例的测试驱动是什么,以及具体要执行的py脚本文件在哪(填写与当前json文件的相对路径即可)// 不填写则在当前json文件下寻找同名py文件// 注意:“py_file”字段当前只能填写一个py文件"driver": {"type": "DeviceTest","py_file": ["Test.py"]}
}
测试套生命周期及配置文件介绍
from devicetest.core.test_case import Step
from devicetest.core.suite.test_suite import TestSuite
class Testsuite1(TestSuite):# 测试套的前置步骤,会在所有的测试用例执行前先运行,对于批量测试用例有共同的前置步骤的诉求可以写在这def setup(self):Step("TestSuite: setup")# 测试套的清理步骤,会在所有测试用例执行完后运行def teardown(self):Step("TestSuite: teardown")
{"description": "Config for OpenHarmony app test suites",//environment字段主要描述测试用例需要的环境信息,如需要多少个设备"environment": [{"type": "device","label": "phone"}],// driver字段主要描述测试用例的测试驱动是什么,以及具体要执行的py脚本文件在哪(填写与当前json文件的相对路径即可)"driver": {"type": "DeviceTestSuite",// 指定测试套json文件对应的py文件路径(可以不加py后缀),可以为相对路径或者绝对路径,如果使用相对路径,需要指定相对测试工程根目录的路径。可以不指定,不指定则直接查找和当前json同目录下同名的py文件"testsuite": "TS_001/TS_001",//指定测试套中的测试用例列表,有两种方式//方式一,定义suitecases字段,然后明确定义好当前测试套下有哪些测试用例(相对路径或者是绝对路径,使用相对路径时根目录为测试套目录)"suitecases": ["XXX_001.py","/path/to/XXX_002.py"]//方式二,测试用例的py文件放在testsuite1文件夹中,并且命名以"TC_"开头,框架即可自动扫描所有用例并执行},// kits字段主要描述测试用例需要的测试公共kit,如pushkit、shellkit等"kits": []
}
测试用例的编写可以有两种方式。
第一种、框架自动扫描的测试用例脚本,需要满足以下规则。
- 与测试套py文件在同一个文件夹目录下;
- 文件名的开头必须是以“TC_”开头;
第二种、需要在json中指定py文件
4、测试用例执行
可以使用IDE执行单条用例,或多条用例,右键放到单条用例上执行单条,放到文件夹上执行当前目录下所有用例
命令行执行
打开命令行窗口,并进入到测试脚本工程的根目录;执行以下命令进入Hypium控制台,即可完成Hypium框架启动:
python -m hypium
Hypium框架指令可以分为三组:help、list、run
help查询命令使用,list指令用来展示设备和相关的任务信息
run指令主要用于执行测试任务
4、常用Api
Api类型
Hypium测试框架提供了两大类API来支持用例的编写。第一类是需要被测设备参数执行的API,第二类是无需被测设备,在PC端可独立调用的API。
设备相关的API主要包括四个基础API类:UiDriver,BY,UiComponent,UiWindow。
- UiDriver类为UI测试的入口,代表了一个被测设备,提供控件查找、控件检查、用户操作模拟、执行shell命令、安装卸载应用等等Ui测试核心能力。
- BY对象用于描述需要操作的控件属性,实现控件定位。
- UiComponent为UiDriver查找返回的控件对象,提供控件属性查询、控件点击、滑动查找等触控/检视能力。
- UiWindow为UiDriver查找返回的窗口对象,提供窗口属性查询、窗口拖动、大小调整等触控能力
from hypium import UiDriver,BY,UiComponent,UiWindow
from hypium.model import WindowFilter# 创建driver对象(self.device1对象在测试用例类中提供)
driver = UiDriver(self.device1)
# 查找控件
component = driver.find_component(BY.text("蓝牙"))
# 查找窗口
window = driver.find_window(WindowFilter().bundle_name("com.huawei.hmos.settings"))
设备无关的API当前主要包括两个基础API类: host 和CV。
- host 提供基础值断言, PC端shell命令执行等PC端基础操作能力。
- CV 提供图像查找,图像比较,压缩,清晰度计算等基础图像操作能力。
from hypium import host, CV# 执行PC端命令
echo = host.shell("a.bat")
# 调用图像接口
brightness = CV.calculate_brightness("/path/to/image.jpeg")
uiviewer使用
选择设备号,点击刷新和确定
元素定位
Hypium中的定位操作目标的方式主要分三大类型,包括控件属性定位,图片匹配定位以及比例坐标定位。根据操作目标的定位准确性,首选方式为控件属性定位,次选图片匹配定位。当无法使用前两类方式定位时,可以选择比例坐标定位操作目标。
属性定位
# 查找text属性为"控件文本"的控件
component = driver.find_component(BY.text("蓝牙"))
# 读取控件的的边框位置
bounds = component.getBounds()
# 直接点击控件
component = driver.touch(BY.text("蓝牙"))
# 点击text属性以`今天星期`开头的控件
driver.touch(BY.text("今天星期", MatchPattern.STARTS_WITH))# 点击文本为"蓝牙", 类型为"Button", 并且key为"bluetooth_switch"的按钮
driver.touch(BY.text("蓝牙").type("Button").key("bluetooth_switch"))# 同样,在查找以及其他可以传入BY对象的接口中可以使用相同的用法
component = driver.find_component(BY.text("蓝牙").type("Button").key("bluetooth_switch"))# 查找在text属性为"显示通知图标"的控件之后的type属性为"Button"的控件
component = driver.find_component(BY.type("Button").isAfter(BY.text("显示通知图标")))
# 查找在text属性为"账号"的控件之前的type属性为"Image"的控件
component = driver.find_component(BY.type("Image").isBefore(BY.text("账号")))
# 查找在key为"nav_container"内部的类型为"Image"的控件
component = driver.find_component(BY.type("Image").within(BY.key("nav_container")))
# 查找包名为"com.huawei.hmos.settings"的应用内部的text属性为"蓝牙"的控件
component = driver.find_component(BY.text("蓝牙").inWindow("com.huawei.hmos.settings"))xpath定位
# 查找上图中红框所示的图标,并点击
comp = driver.find_component(BY.xpath("//*[@text='可用 WLAN']/ancestor::List/ListItemGroup/ListItem[1]//Text/following::Image"))
comp.click()# 查找text属性为WLAN的控件
driver.find_component(BY.xpath("//*[@text='WLAN']"))
driver.find_all_components(BY.xpath("//*[@text='WLAN']"))
driver.wait_for_component(BY.xpath("//*[@text='WLAN']"))
# 点击text属性为WLAN的控件
driver.touch(BY.xpath("//*[@text='WLAN']"))查找多个元素
# 查找所有type属性为"Button"的控件, 如果有匹配的结果,components为列表,包含多个满足条件的UiComponent对象
components = driver.find_all_components(BY.type("Button"))
# 点击所有的控件
for component in components:driver.touch(component)
# 点击第2个控件
driver.touch(component[1])坐标定位
# 点击屏幕上(0.52 * 屏幕宽度, 0.98 * 屏幕高度)的位置
driver.touch((0.52, 0.98))
图片定位需要先安装opencv库
pip install opencv-python# 点击屏幕上和模板图片template.jpeg匹配的位置
driver.touch_image("/path/to/template.jpeg")# 查找屏幕上和模板图片template.jpeg匹配的位置, bounds为Rect类型,记录了控件上下左右边框的位置
bounds = driver.find_image("template.jpeg")
print(bounds.top, bounds.left, bounds.bottom, bounds.right)
窗口查找
# 查找标题为日历的窗口
window = driver.find_window(WindowFilter().title("日历"))
# 查找包名为com.ohos.calender,并且处于活动状态的窗口
window = driver.find_window(WindowFilter().bundle_name("com.ohos.calendar").actived(True))
# 查找处于活动状态的窗口
window = driver.find_window(WindowFilter().actived(True))
# 查找聚焦状态的窗口
window = driver.find_window(WindowFilter().focused(True))
元素操作
touch操作
# 点击文本为"hello"的控件
driver.touch(BY.text("hello"))
# 点击(100, 200)的位置
driver.touch((100, 200))
# 点击比例坐标为(0.8, 0.9)的位置
driver.touch((0.8, 0.9))
# 双击确认按钮(控件文本为"确认", 类型为"Button")
driver.touch(BY.text("确认").type("Button"), mode=UiParam.DOUBLE)
# 在类型为Scroll的控件上滑动查找文本为"退出"的控件并点击
driver.touch(BY.text("退出"), scroll_target=BY.type("Scroll"))
# 长按比例坐标为(0.8, 0.9)的位置
driver.touch((0.8, 0.9), mode="long")多指操作
# 执行多指点击操作, 同时点击屏幕(0.1, 0.2), (0.3, 0.4)的位置
driver.multi_finger_touch([(0.1, 0.2), (0.3, 0.4)])
# 执行多指点击操作, 设置点击按下时间为1秒
driver.multi_finger_touch([(0.1, 0.2), (0.3, 0.4)], duration=2)
# 查找Image类型控件
comp = driver.find_component(BY.type("Image"))
# 在指定的控件区域内执行多指点击(点击坐标为控件区域内的比例坐标)
driver.multi_finger_touch([(0.5, 0.5), (0.6, 0.6)], area=comp.getBounds())滑动
# 在屏幕上向上滑动, 距离40
driver.swipe(UiParam.UP, distance=40)
# 在屏幕上向右滑动, 滑动事件为0.1秒
driver.swipe(UiParam.RIGHT, swipe_time=0.1)
# 在屏幕起始点为比例坐标为(0.8, 0.8)的位置向上滑动,距离30
driver.swipe(UiParam.UP, 30, start_point=(0.8, 0.8))
# 在屏幕左边区域向下滑动, 距离30
driver.swipe(UiParam.DOWN, 30, side=UiParam.LEFT)
# 在屏幕右侧区域向上滑动,距离30
driver.swipe(UiParam.UP, side=UiParam.RIGHT)
# 在类型为Scroll的控件中向上滑动
driver.swipe(UiParam.UP, area=BY.type("Scroll"))精准滑动
# 从类型为Slider的控件滑动到文本为最大的控件
driver.slide(BY.type("Slider"), BY.text("最大"))
# 从坐标100, 200滑动到300,400
driver.slide((100, 200), (300, 400))
# 从坐标100, 200滑动到300,400, 滑动时间为3秒
driver.slide((100, 200), (300, 400), slide_time=3)
# 在类型为Slider的控件上从(0, 0)滑动到(100, 0)
driver.slide((0, 0), (100, 0), area = BY.type("Slider"))执行hdc命令# 执行hdc命令list targets
echo = driver.hdc("list targets")
# 执行hdc命令hilog, 设置30秒超时
echo = driver.hdc("hilog", timeout = 30)执行shell命令
# 在设备shell中执行命令ls -l
echo = driver.shell("ls -l")
# 在设备shell中执行命令top, 设置10秒超时时间
echo = driver.shell("top", timeout=10)安装卸载
# 安装路径为test.hap的安装包到手机
driver.install_app(r"test.hap")
# 替换安装路径为test.hap的安装包到手机(增加-r参数指定替换安装)
driver.install_app(r"test.hap", "-r")
driver.uninstall_app(driver, "com.ohos.devicetest")清除缓存
# 清除包名为com.tencent.mm的应用的缓存数据
driver.clear_app_data("com.tencent.mm")启动app/停止app
# 启动浏览器
driver.start_app("com.huawei.hmos.browser", "MainAbility")# 停止com.ohos.settings
driver.stop_app("com.ohos.settings")
断言
# 检查a等于b
host.check(a, b, "a != b")
# 检查a大于b
host.check_greater(a, b)# 检查类型为Button的控件存在
driver.check_component_exist(BY.type("Button"))
# 检查类型为Button的控件存在,如果不存在等待最多5秒
driver.check_component_exist(BY.type("Button"), wait_time=5)
# 在类型为Scroll的控件上滚动检查文本为"hello"的控件存在
driver.check_component_exist(BY.text("hello"), scroll_target=BY.type("Scroll"))
# 检查文本为确认的控件不存在
driver.check_component_exist(BY.text("确认"), expect_exist=False)# 检查id为xxx的控件的checked属性为True
driver.check_component(BY.key("xxx"), checked=True)
# 检查id为check_button的按钮enabled属性为True
driver.check_component(BY.key("checked_button"), enabled=True)
# 检查id为container的控件文本内容为正在检查
driver.check_component(BY.key("container"), text="正在检查")
# 检查id为container的控件文本内容不为空
driver.check_component(BY.key("container"), text="", expect_equal=False)# 检查图片存在
driver.check_image_exist("test.jpeg")
# 检查图片不存在
driver.check_image_exist("test.jpeg", expect_exist=False)
# 检查图片存在, 图片相似度要求95%, 重复检查时间5秒
driver.check_image_exist("test.jpeg", timeout=5, similarity=0.95)
# 检查图片不存在, 重复检查时间5秒
driver.check_image_exist("test.jpeg", timeout=5, expect_exist=False)
# 使用sift算法检查图片存在, 设置最少匹配特征点数量为16
driver.check_image_exist("test.jpeg", mode="sift", min_match_point=16)
5、小demo
# !/usr/bin/env python
# coding: utf-8
"""
#!!================================================================
#版权 (C) 2023, Huawei Technologies Co.
#==================================================================
#文 件 名: Example.py
#文件说明: Example TestScript
#作 者: author
#生成日期: 2023-07-13
#!!================================================================
"""
import timefrom devicetest.core.test_case import TestCase, Step
from hypium import *
from hypium.model import UiParamclass Example(TestCase):def __init__(self, controllers):self.TAG = self.__class__.__name__TestCase.__init__(self, self.TAG, controllers)self.driver = UiDriver(self.device1)def setup(self):Step('1.回到桌面')self.driver.swipe_to_home()def process(self):Step('2.启动设置应用')self.driver.start_app(package_name="com.huawei.hmos.settings", page_name="com.huawei.hmos.settings.MainAbility")//上滑40self.driver.swipe(UiParam.UP,40)//点击系统self.driver.find_component(BY.text("系统")).click()//返回self.driver.go_back()//下滑time.sleep(1)self.driver.swipe(UiParam.DOWN,40)//找到蓝牙控件点击进入lanYa = self.driver.find_component(BY.key("bluetooth_entry.title"))lanYa.click()//点击打开蓝牙开关lanYaKaiGuan = self.driver.find_component(BY.type("Toggle"))lanYaKaiGuan.click()//断言蓝牙开关是否打开self.driver.check_component(lanYaKaiGuan,checked=True)self.driver.go_back()def teardown(self):Step('3.关闭设置应用')self.driver.stop_app("com.huawei.hmos.settings")
6、报告及日志
执行完毕后,会在当前目录下生成对应的reports文件夹,打开对应的报告,下边有个summary_report.html文件用浏览器打开,可以查看日志
报告总结,可以看到多少成功和失败
每个用例详情,可以看到是哪一步出错了,还有具体截图和报错日志