鸿蒙5.0开发【应用UI测试(基于python)】测试框架_鸿蒙自动化测试框架
框架概述
DevEco Testing Hypium(以下简称Hypium)是HarmonyOS平台的UI自动化测试框架,支持开发者使用python语言为应用编写UI自动化测试脚本,主要包含以下特性:
- Hypium提供了原生控件/图像/比例坐标等多种控件定位能力,支持多窗口操作以及触摸屏/鼠标/键盘等多种模拟输入功能,支持多设备并行操作,能够覆盖各类场景和多种形态设备上的自动化用例编写需求。
- Hypium包含配套用例编写辅助插件, 支持控件查看/投屏操作等多种用例开发辅助功能,提升用例开发体验和效率。
- Hypium能够为执行的用例生成详细的用例执行报告,并且自动记录设备日志以及执行步骤截图,为开发者和测试人员提供高效和专业的测试用例执行和结果分析体验。
安装向导
1.Python安装
推荐从官网获取3.10版本,其他版本可能出现兼容性问题
https://www.python.org/
pip源配置:
Ⅰ.在用户目录下的pip目录中创建pip.ini,配置pip源为可以正常访问的pip源
Ⅱ. pip.ini内容如下:
[global]index-url = http://xxxx/pypi/simpletrusted-host = xxxx
Ⅲ. 在CMD命令窗口输入 python -m pip install --upgrade pip 更新pip
2.IDE安装
推荐从pycharm官网获取2022.3以后的社区版本:
https://www.jetbrains.com.cn/en-us/pycharm/
3.HDC安装
下载DevEco Studio获取,配置向导默认下载Toolchains获取
4.Hypium安装
访问华为开发者联盟官网下载页面:
在页面最底部下载Hypium安装包,解压后找到其中的其中的hypium-5.0.7.200.zip(请以实际版本号为准)。
解压后该文件后得到的4个tar.gz格式的pip安装包,使用pip install命令安装。
Hypium安装对xdevice有依赖,优先安装xdevice,以下版本号仅做示例,请以实际版本号为准。
pip install xdevice-5.0.7.200.tar.gzpip install xdevice-devicetest-5.0.7.200.tar.gzpip install xdevice-ohos-5.0.7.200.tar.gzpip install hypium-5.0.7.200.tar.gz
5.DevEco Testing Hypium插件安装及使用方法
注意:Mac系统使用UIViewer功能时,必须在设置面板中手动指定Hdc路径,详情可见 · 插件功能 Ⅳ.设置面板区域-Hdc路径。
- 插件安装
Ⅰ. 准备DevEco Testing Hypium件离线安装包,下载完成后不需要解压。
Ⅱ. 打开pycharm后,点击File -> Settings -> Plugin -> 齿轮图标 -> Install Plugin from Disk -> 选中刚刚下载的离线安装zip包 -> 安装完成后重启pycharm。
安装完成后会有如下内容:
- 插件功能
插件功能按功能区域进行区分,分为三个区域
Ⅰ. 项目文件区域
在项目文件区域选中
Ⅱ. 代码编辑区域
Ⅲ. ToolWindow区域
UIViewer功能
pycharm界面右侧栏的toolWindow区域有UIViewer标签,点击后会展开UIViewer面板。UIViewer功能目前分为4个界面: 设备选择界面 、单设备控件查看界面 、单设备投屏界面 、双设备投屏界面。
设备选择界面
如果是第一次进入此界面,或设备有变动,需要点击\"刷新\"按钮进行设备刷新
注:当前仅支持USB连接本地设备调测,暂不支持模拟器。
- 设备选择界面最多同时支持选择两个设备进入投屏状态。
- 若勾选了两个设备后点击\"确定\"按钮,则会进入双设备投屏界面。进入双设备投屏界面时,需要保证两个设备的\"设备编号\"不同,进入双设备投屏界面后,设备编号为\"dev1\"的设备会显示在左侧,设备编号为\"dev2\"的设备会显示在右侧。
- 若勾选了一个设备后点击\"确定\"按钮,则会进入单设备投屏界面
单设备投屏界面
布局说明:
- 镜像投屏:点击后进入镜像投屏模式,此模式下可以使用鼠标对显示界面进行点击、滑动操作;只有在此模式下才能进入控件查看模式。
- 设备切换:点击后回到设备选择界面。
- shell命令输入:在输入框输入shell命令之后点击确定,插件便会在当前设备上执行shell命令,插件会自动给命令头部添加\"hdc shell\",同时请勿执行阻塞性命令。
- 设备显示界面:显示当前设备的屏幕画面,处于投屏状态时,可以用鼠标点击和滑动此界面来控制设备。
- 工具区:设备显示界面右侧为工具区,工具区从上至下分为以下3部分:
工具区1:
此工具区提供控件查看的功能,处于控件查看模式时,设备画面不再变动。若要重新获取当前页面的信息,需要点击控件刷新按钮。
工具区2:
此工具区提供一些设备操作的功能,目前提供的功能如下:
工具区3:
此工具区的按钮主要提供一些与设备系统交互的功能,目前提供的功能如下:
双设备投屏界面
此界面的功能与单设备的投屏(控件)界面功能相同,只是展示设备的数量变为2个。对某个设备进行控件查看时,会自动退出双设备投屏界面,回到单设备的控制查看界面。
执行结果报告展示功能
使用一键执行当前用例功能后,当用例执行完成,插件会在控制台旁生成一个标签,点击后可以看到用例的执行步骤图片。
Ⅳ. 设置面板区域
打开pycharm设备面板,可以看到Deveco Testing Hypium的设置选项,点击后的图片如下:
Ⅴ. 工程创建区域
在pycharm顶部点击File -> new project :
可以看到pycharm提供的模板创建工程中有DevEco Testing Hypium,此处提供两种类型的Hypium模板工程创建,分别为单设备和双设备的场景:
点击其中一个模板后便会创建Hypium模板工程,工程其中包含了一个模板用例和一个模板user_config.xml,正常情况下用户无需改动。下面以单设备工程为例,创建完成后的界面如下,接入设备后,右键一键执行便可执行当前用例:
测试脚本开发快速入门
本章节适用于Hypium测试脚本开发的初学者。通过创建一个简单的测试脚本工程,快速了解工程目录的主要文件,熟悉Hypium测试脚本的开发流程。
- 测试脚本工程创建
测试脚本工程创建主要有两种方式:
a)直接利用以下附件中的模板工程;
[HypiumProjectTemplate.zip]
b)通过pycharm上的Hypium插件进行创建;
Ⅰ.工程目录文件介绍
HypiumProjectTemplate| |----aw // 工程中自定义模块文件夹| | |----Utils.py // 示例模块文件| |----config // 测试工程配置文件夹| | |----user_config.xml // 测试工程配置文件,主要是测试框架的任务配置| |----resource // 测试资源文件夹,测试过程中用到的资源文件默认会优先从当前文件夹进行查找| |----testcases // 测试用例文件夹,测试过程中的测试用例文件优先会从当前文件夹进行查找| | |----Example.json // Example测试用例配置文件,配置用例设备信息等| | |----Example.py // Example测试用例文件,实际的测试逻辑代码| |----main.py // 测试用例执行入口
Ⅱ.工程目录文件介绍
DEBUG /data/log/tee;/data/log/test DEBUG ON
Ⅲ.测试用例介绍
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, Stepfrom devicetest.utils.file_util import get_resource_pathfrom hypium import *from aw import Utilsclass 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.检查短信应用版本\') mms_version = Utils.get_app_version_code(self.driver, \'com.ohos.mms\') host.check_greater(mms_version, 0) Step(\'3.点击桌面上的短信\') self.driver.touch(BY.text(\"信息\")) def teardown(self): Step(\"4. 停止短信应用\") self.driver.stop_app(\"com.ohos.mms\")
测试用例配置文件
该文件主要描述测试套的测试配置信息,比如测试套需要多少个设备、测试套的测试驱动信息、测试套的描述信息等,示例如下:
{ \"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 Stepfrom devicetest.core.suite.test_suite import TestSuiteclass 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文件,示例参考上述章节中的测试用例编写规范。
- 测试用例执行
本章节主要介绍测试用例如何执行,测试用例执行有两种方式,一种是通过命令行方式执行用例,一种是通过pycharm IDE上的Hypium插件一键执行。
Ⅰ.命令介绍
Hypium框架指令可以分为三组:help、list、run。在指令序列中,以run为最常用的执行指令。
命令交互入口
打开命令行窗口,并进入到测试脚本工程的根目录;执行以下命令进入Hypium控制台,即可完成Hypium框架启动:
python -m hypium
Ⅱ. 常用命令介绍
help命令
输入help指令可以查询框架指令帮助信息。
help: use help to get information. usage: run: Display a list of supported run command. list: Display a list of supported device and task record. Examples: help run help list
说明: help run:展示run指令相关说明;help list:展示 list指令相关说明。
list命令
list指令用来展示设备和相关的任务信息。
list: This command is used to display device list and task record. usage: list list history list Introduction: list: display device list list history: display history record of a serial of tasks list : display history record about task what contains specific id Examples: list list history list 6e****90
说明: list: 展示设备信息;list history: 展示任务历史信息;list : 展示特定id的任务其历史信息。
run命令
run指令主要用于执行测试任务。
run: This command is used to execute the selected testcases. It includes a series of processes such as use case compilation, execution, and result collection. usage: run [-l TESTLIST [TESTLIST ...] | -tf TESTFILE [TESTFILE ...]] [-tc TESTCASE] [-c CONFIG] [-sn DEVICE_SN] [-rp REPORT_PATH [REPORT_PATH ...]] [-respath RESOURCE_PATH [RESOURCE_PATH ...]] [-tcpath TESTCASES_PATH [TESTCASES_PATH ...]] [-ta TESTARGS [TESTARGS ...]] [-env TEST_ENVIRONMENT [TEST_ENVIRONMENT ...]] [--retry RETRY] [--session SESSION] [--repeat REPEAT] action task Specify tests to run. positional arguments: action Specify action task Specify task name,such as \"ssts\", \"acts\", \"hits\"
run常用指令基本使用方式如下:
Ⅲ. IDE上用例执行
具体IDE上如何可视化执行参考Hypium上插件使用方法。
Ⅳ. 测试报告查看
测试框架执行完用例后,会生成对应的log文件,还会生成对应的执行结果报告。如果使用了-rp参数指定报告路径,那么报告就会生成在指定的路径下。否则报告会存放在默认目录(工程目录的reports文件夹下)。
当前报告目录(默认目录/指定目录) ├── details(用例步骤截图存放目录) ├── result(模块执行结果存放目录) │ ├── .xml │ ├── ... ... ├── log (设备和任务运行log存放目录) │ ├── .log │ ├── ... ... │ ├── .log ├── static (报告展示页面css元素存放目录) ├── summary_report.html(测试任务可视化报告) ├── summary_report.xml(测试任务数据报告) ├── summary.ini(记录测试类型,使用的设备,开始时间和结束时间等信息) ├── task_info.record(记录执行命令,失败用例等清单信息)
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,UiWindowfrom 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\")
此外Hypium定义的一些常量类型,例如KeyCode,UiParam,MatchPattern等,以及数据类型Point,Rect等,这部分包含在hypium.model包中。
示例代码
from hypium.model import KeyCode, UiParam, MatchPattern# 按下电源键(使用常量KeyCode.POWER)driver.press_key(KeyCode.POWER)# 向左滑动屏幕(使用常量UiParam.LEFT)driver.swipe(UiParam.LEFT)
下文各小节将详细介绍主要测试场景中Hypium的API使用方法。
API使用方法
- 控件查看
使用DevEco Testing Hypium插件中的UIViewer工具即可查看控件的各种属性。
- 控件查找
Hypium中的定位操作目标的方式主要分三大类型,包括控件属性定位,图片匹配定位以及比例坐标定位。根据操作目标的定位准确性,首选方式为控件属性定位,次选图片匹配定位。当无法使用前两类方式定位时,可以选择比例坐标定位操作目标。
Hypium中的控件属性定位通过BY选择器对象来实现,接下来将介绍使用控件属性定位控件,及使用图片匹配和比例坐标定位控件。
单属性定位控件
从hypium包中导入BY选择器对象, 从hypium.model包中导入匹配模式常量类MatchPattern
from hypium import BYfrom hypium.model import MatchPattern
通过BY对象可以指定需要查找/操作的控件对象。
# 查找text属性为\"控件文本\"的控件component = driver.find_component(BY.text(\"蓝牙\"))# 读取控件的的边框位置bounds = component.getBounds()# 直接点击控件component = driver.touch(BY.text(\"蓝牙\"))
注意: 默认情况下,find_component和touch等方法会查找/操作第一个条件匹配的控件,如需操作第n个满足匹配条件的控件,请参考查找所有匹配控件
当前控件的text属性支持三种模糊匹配方式,通过BY.text方法第二个可选参数指定,如下表所示:
# 点击text属性以`今天星期`开头的控件driver.touch(BY.text(\"今天星期\", MatchPattern.STARTS_WITH))
BY选择器支持的所有属性如下表所示。
多属性组合定位控件
BY选择器支持使用链式调用的方式指定多个属性来定位一个控件,在多个控件有部分属性相同,部分属性不同时可以更精确地定位控件。
# 点击文本为\"蓝牙\", 类型为\"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\"))
控件相对位置+属性组合定位控件
对于某些自身属性不唯一,无法精确定位的控件,BY选择器支持通过与其他控件的相对位置关系来提高定位控件的精确性。支持的相对定位方式如下表所示。
相对位置通常和控件的属性结合使用来定位控件,以下图场景为例,界面上存在多个按钮,我们需要点击显示通知图标之后的按钮,定位方式如下:
- 首先选择一个可以通过属性唯一定位的锚点控件。例如BY.text(“显示通知图标”)
- 然后找到想要操作的目标控件,选择该控件的一个不唯一属性(通常为type属性)。例如BY.type(“Button”)
- 使用相对位置接口来描述锚点控件和目标控件的位置的关系,得到完整的控件选择器。例如BY.type(“Button”).isAfter(BY.text (“显示通知图标”)) ,注意到这里组合使用了type属性和isAfter相对位置接口。
其余相对定位的方式使用方法类似。
示例代码
# 查找在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\"))
注意: 相对位置中的锚点控件不能再使用相对位置描述,即BY.isBefore方法的参数中不能在出现BY.isBefore或者BY.isAfter等相对定位的方式。
# 以下代码无法正常执行, 因为BY.isBefore的参数BY对象中不能使用BY.isAfter等相对定位的方式component = driver.find_component(BY.type(\"Image\").isBefore(BY.type(\"Button\").isAfter(BY.text(\"蓝牙\"))))
xpath方式查找匹配的控件
部分控件没有唯一定位的属性,同时通过相对定位的方式也无法准确定位,此时可以使用xpath语法来进行更新精确的控件定位。
使用BY.xpath匹配器可以支持通过xpath语法来查找控件。注意xpath不能和其他匹配器一起使用,并且通过xpath查找控件会慢一些。
在如下场景中,需要找到红框标识的图标,然而该图标没有唯一定位的属性,此时可以使用xpath语法描述该控件相对其他可定位控件的路径关系来定位该控件。
该页面上,\"可用 WLAN\"是一个固定的可唯一定位的文本,我们首先通过xpath定位到该文本 //*[@text=‘可用 WLAN’] ,然后在找这个节点所在的List控件 /ancestor::List,然后从这个List控件开始找到对应的Image控件 /ListItemGroup/ListItem[1]//Text//following::Image。
示例代码
# 查找上图中红框所示的图标,并点击comp = driver.find_component(BY.xpath(\"//*[@text=\'可用 WLAN\']/ancestor::List/ListItemGroup/ListItem[1]//Text/following::Image\"))comp.click()
在支持传入BY选择器的接口上都可以使用xpath来定位控件
# 查找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\']\"))
查找所有匹配控件
默认情况下,find_component接口和其他支持传入BY选择器的接口会查找第一个匹配的控件进行操作,使用driver.find_all_components接口可以返回所有匹配查找条件的控件,脚本开发人员可以根据需要选择其中的某个控件进行操作,或者对所有控件进行操作。
以如下场景为例,界面上存在多个Button,我们需要点击第n个特定的Button或者点击所有Button,则可以使用driver.find_all_components
示例代码
# 查找所有type属性为\"Button\"的控件, 如果有匹配的结果,components为列表,包含多个满足条件的UiComponent对象components = driver.find_all_components(BY.type(\"Button\"))# 点击所有的控件for component in components: driver.touch(component)# 点击第2个控件driver.touch(component[1])
图片定位控件
如果控件没有可用于定位的唯一属性,通过相对位置也无法实现定位,可以通过截取控件的图片,然后使用driver.find_image来查找控件的位置,或者使用driver.touch_image接口来直接点击控件。
例如如下场景,如果红框中的控件没有可供唯一定位的属性,同时附近的控件也无法唯一定位导致不能使用相对位置定位时,可以尝试使用图片匹配的方案定位控件,定位方式如下:
- 截取红框中图片保存到为template.jpeg(文件名根据需要定义)
- 调用driver.touch_image,传入template.jpeg图片的路径
注意: 使用图片定位控件必须安装opencv-python包,使用如下命令安装:
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)
注意: 当前仅支持查找匹配度最高的第一个匹配的图片区域,不支持匹配多个目标。
比例坐标定位控件
如果图像特征不明显,使用图像匹配的方式也无法准确识别控件位置,可以通过比例坐标的方式点击控件。注意该方式不推荐使用,因为在屏幕比例或者控件位置发生变化时该种方式通常会失效,无法操作到正确的控件,并且如果实际操作界面不在当前界面,点击也会成功,但实际效果不符合预期。如果必须使用,通常需要保证其前一步或者后一步是通过控件属性来定位控件的,避免脚本执行非预期操作而框架无法感知。
例如下图场景中,如果红框中的控件无法通过上述方式定位,可以采用比例坐标的方式点击(不推荐使用)
比例坐标可通过DevEco Testing Hypium插件查看
# 点击屏幕上(0.52 * 屏幕宽度, 0.98 * 屏幕高度)的位置driver.touch((0.52, 0.98))
- 窗口查找
查找窗口
def find_window(filter: WindowFilter) -> UiWindow
接口说明
根据指定条件查找窗口,返回窗口对象
参数说明
返回值
如果找到window则返回UiWindow对象,否则返回None
使用示例
# 查找标题为日历的窗口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))
- 界面操作
Ⅰ.触摸屏
点击
def touch(target: Union[By, UiComponent, tuple], mode: str = \"normal\", scroll_target: Union[By, UiComponent] = None, wait_time: float = 0.1)
接口说明
根据选定的控件或者坐标位置执行点击操作
参数说明
使用示例
# 点击文本为\"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\")
多指点击
def multi_finger_touch(points: List[tuple], duration: float = 0.1,area: Rect = None)
接口说明
执行多指点击操作
参数说明
使用示例
# 执行多指点击操作, 同时点击屏幕(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())
滑动
执行不太精准的滑动操作
def swipe(direction: str, distance: int = 60, area: Union[By, UiComponent] = None, side: str = None, start_point: tuple = None, swipe_time: float = 0.3)
接口说明
在屏幕上或者指定区域area中执行朝向指定方向direction的滑动操作。该接口用于执行不太精准的滑动操作。
参数说明
使用示例
# 在屏幕上向上滑动, 距离40driver.swipe(UiParam.UP, distance=40)# 在屏幕上向右滑动, 滑动事件为0.1秒driver.swipe(UiParam.RIGHT, swipe_time=0.1)# 在屏幕起始点为比例坐标为(0.8, 0.8)的位置向上滑动,距离30driver.swipe(UiParam.UP, 30, start_point=(0.8, 0.8))# 在屏幕左边区域向下滑动, 距离30driver.swipe(UiParam.DOWN, 30, side=UiParam.LEFT)# 在屏幕右侧区域向上滑动,距离30driver.swipe(UiParam.UP, side=UiParam.RIGHT)# 在类型为Scroll的控件中向向滑动driver.swipe(UiParam.UP, area=BY.type(\"Scroll\"))
执行精准的滑动操作
def slide(start: U nion[By, tuple], end: Union[By, tuple], area: Union[By, UiComponent] = None, slide_time: float = DEFAULT_SLIDE_TIME)
接口说明
根据指定的起始和结束位置执行滑动操作,起始和结束的位置可以为控件或者屏幕坐标。该接口用于执行较为精准的滑动操作。
参数说明
使用示例
# 从类型为Slider的控件滑动到文本为最大的控件driver.slide(BY.type(\"Slider\"), BY.text(\"最大\"))# 从坐标100, 200滑动到300,400driver.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\"))
拖拽
def drag(start: Union[By, tuple, UiComponent], end: Union[By, tuple, UiComponent], area: Union[By, UiComponent] = None, press_time: float = 1, drag_time: float = 1)
接口说明
根据指定的起始和结束位置执行拖拽操作,起始和结束的位置可以为控件或者屏幕坐标
参数说明
使用示例
# 拖拽文本为\"文件.txt\"的控件到文本为\"上传文件\"的控件driver.drag(BY.text(\"文件.txt\"), BY.text(\"上传文件\"))# 拖拽id为\"start_bar\"的控件到坐标(100, 200)的位置, 拖拽时间为2秒driver.drag(BY.key(\"start_bar\"), (100, 200), drag_time=2)# 在id为\"Canvas\"的控件上从相对位置(10, 20)拖拽到(100, 200)driver.drag((10, 20), (100, 200), area = BY.id(\"Canvas\"))# 在滑动条上从相对位置(10, 10)拖拽到(10, 200)driver.drag((10, 10), (10, 200), area=BY.type(\"Slider\"))
捏合缩小
def pinch_in(area: Union[By, UiComponent, Rect], scale: float = 0.4, direction: str = \"diagonal\", **kwargs)
接口说明
在控件上捏合缩小
参数说明
使用示例
# 在类型为Image的控件上进行双指捏合缩小操作driver.pinch_in(BY.type(\"Image\"))# 在类型为Image的控件上进行双指捏合缩小操作, 设置水平方向捏合driver.pinch_in(BY.type(\"Image\"), direction=\"horizontal\")
双指放大
def pinch_out(area: Union[By, UiComponent, Rect], scale: float = 1.6, direction: str = \"diagonal\", **kwargs)
接口说明
在控件上双指放大
参数说明
使用示例
# 在类型为Image的控件上进行双指放大操作driver.pinch_out(BY.type(\"Image\"))# 在类型为Image的控件上进行双指捏合缩小操作, 设置水平方向捏合driver.pinch_out(BY.type(\"Image\"), direction=\"horizontal\")
双指滑动
def two_finger_swipe(start1: tuple, end1: tuple, start2: tuple, end2: tuple, duration: float = 0.5, area: Rect = None)
接口说明
执行双指滑动操作
参数说明
使用示例
# 执行双指滑动操作, 手指1从(0.4, 0.4)滑动到(0.2, 0.2), 手指2从(0.6, 0.6)滑动到(0.8, 0.8)driver.two_finger_swipe((0.4, 0.4), (0.2, 0.2), (0.6, 0.6), (0.8, 0.8))# 执行双指滑动操作, 手指1从(0.4, 0.4)滑动到(0.2, 0.2), 手指2从(0.6, 0.6)滑动到(0.8, 0.8), 持续时间3秒driver.two_finger_swipe((0.4, 0.4), (0.2, 0.2), (0.6, 0.6), (0.8, 0.8), duration=3)# 查找Image类型控件comp = driver.find_component(BY.type(\"Image\"))# 在指定的控件区域内执行双指滑动(滑动起始/停止坐标为控件区域内的比例坐标)driver.two_finger_swipe((0.4, 0.4), (0.1, 0.1), (0.6, 0.6), (0.9, 0.9), area=comp.getBounds())
自定路径滑动手势(单指)
def inject_gesture(gesture: Gesture, speed: int = 2000)
接口说明
执行自定义滑动手势操作
参数说明
使用示例
from hypium.uidriver import Gesture# 创建一个gesture对象gesture = Gesture()# 获取控件计算器的位置pos = driver.findComponent(BY.text(\"计算器\")).getBoundsCenter()# 获取屏幕尺寸size = driver.getDisplaySize()# 起始位置, 长按2秒gesture.start(pos.to_tuple(), 2)# 移动到屏幕边缘gesture.move_to(Point(size.X - 20, int(size.Y / 2)).to_tuple())# 停留2秒gesture.pause(2)# 移动到(360, 500)的位置gesture.move_to(Point(360, 500).to_tuple())# 停留2秒结束gesture.pause(2)# 执行gesture对象描述的操作driver.inject_gesture(gesture)
自定路径滑动手势(多指)
def inject_multi_finger_gesture(gestures: List[Gesture], speed: int = 6000)
接口说明
注入多指手势操作
参数说明
使用示例
from hypium.uidriver import Gesture# 创建手指1的手势, 从(0.4, 0.4)的位置移动到(0.2, 0.2)的位置gesture1 = Gesture().start((0.4, 0.4)).move_to((0.2, 0.2), interval=1)# 创建手指2的手势, 从(0.6, 0.6)的位置移动到(0.8, 0.8)的位置gesture2 = Gesture().start((0.6, 0.6)).move_to((0.8, 0.8), interval=1)# 注入多指操作driver.inject_multi_finger_gesture((gesture1, gesture2))
Ⅱ. 键盘鼠标
鼠标点击
def mouse_click(pos: Union[tuple, UiComponent, By], button_id: MouseButton = MouseButton.MOUSE_BUTTON_LEFT, key1: Union[KeyCode, int] = None, key2: Union[KeyCode, int] = None)
接口说明
鼠标点击, 支持键鼠组合操作
参数说明
使用示例
# 使用鼠标左键长按(100, 200)的位置driver.mouse_long_click((100, 200), MouseButton.MOUSE_BUTTON_LEFT)# 使用鼠标右键长按文本为\"确认\"的控件driver.mouse_long_click(BY.text(\"确认\"), MouseButton.MOUSE_BUTTON_RIGHT)# 使用鼠标右键长按比例坐标(0.8, 0.5)的位置driver.mouse_long_click((0.8, 0.5), MouseButton.MOUSE_BUTTON_RIGHT)
鼠标拖拽
def mouse_drag(start: Union[tuple, UiComponent, By], end: Union[tuple, UiComponent, By], speed: int = 3000)
接口说明
使用鼠标进行拖拽操作(按住鼠标左键移动鼠标)
参数说明
使用示例
# 鼠标从控件1拖拽到控件2driver.mouse_drag(BY.text(\"控件1\"), BY.text(\"控件2\"))
鼠标移动
def mouse_move(start: Union[tuple, UiComponent, By], end: Union[tuple, UiComponent, By], speed: int = 3000)
接口说明
鼠标指针从之前起始位置移动到结束位置,模拟移动轨迹和速度
参数说明
使用示例
# 鼠标从控件1拖拽到控件2driver.mouse_drag(BY.text(\"控件1\"), BY.text(\"控件2\"))
按键
def press_key(key_code: Union[KeyCode, int], key_code2: Union[KeyCode, int] = None, mode=\"normal\")
接口说明
按下指定按键(按组合键请使用press_combination_key)
参数说明
使用示例
# 按下电源键driver.press_key(KeyCode.POWER)# 长按电源键driver.press_key(KeyCode.POWER, mode=UiParam.LONG)# 按下音量下键driver.press_key(KeyCode.VOLUME_DOWN)
按组合键
def press_combination_key(key1: Union[KeyCode, int], key2: Union[KeyCode, int], key3: Union[KeyCode, int] = None)
接口说明
按下组合键, 支持2键或者3键组合
参数说明
使用示例
# 按下音量下键和电源键的组合键driver.press_combination_key(KeyCode.VOLUME_DOWN, KeyCode.POWER)# 同时按下ctrl, shift和F键driver.press_combination_key(KeyCode.CTRL_LEFT, KeyCode.SHIFT_LEFT, KeyCode.F)
- hdc/shell命令执行
hdc命令执行
def hdc(cmd, timeout: float = 60) -> str
接口说明
执行hdc命令
参数说明
返回值
命令执行后的回显内容
使用示例
# 执行hdc命令list targetsecho = driver.hdc(\"list targets\")# 执行hdc命令hilog, 设置30秒超时echo = driver.hdc(\"hilog\", timeout = 30)
设备侧shell命令执行
def shell(cmd: str, timeout: float = 60) -> str
接口说明
在设备端shell中执行命令
参数说明
返回值
命令执行后的回显内容
使用示例
# 在设备shell中执行命令ls -lecho = driver.shell(\"ls -l\")# 在设备shell中执行命令top, 设置10秒超时时间echo = driver.shell(\"top\", timeout=10)
PC侧shell命令执行
def shell(cmd: Union[str, list], timeout: float = 300) -> str
接口说明
在PC端执行shell命令
参数说明
使用示例
# 在PC端执行dir命令echo = host.shell(\"dir\")# 在PC端执行netstat命令读取回显结果, 设置超时时间为10秒echo = host.shell(\"netstat\", timeout=10)
- 应用预置操作
安装应用
def install_app(package_path: str, options: str = \"\", **kwargs)
接口说明
安装app
参数说明
使用示例
# 安装路径为test.hap的安装包到手机driver.install_app(r\"test.hap\")# 替换安装路径为test.hap的安装包到手机(增加-r参数指定替换安装)driver.install_app(r\"test.hap\", \"-r\")
卸载应用
def uninstall_app(package_name: str, **kwargs)
接口说明
卸载App
参数说明
使用示例
driver.uninstall_app(driver, \"com.ohos.devicetest\")
清除应用缓存数据
def clear_app_data(package_name: str)
接口说明
清除app的缓存数据
参数说明
使用示例
# 清除包名为com.tencent.mm的应用的缓存数据driver.clear_app_data(\"com.tencent.mm\")
启动应用
def start_app(package_name: str, page_name: str = None, params: str = \"\", wait_time: float = 1)
接口说明
根据包名启动指定的app
参数说明
使用示例
# 启动浏览器driver.start_app(\"com.huawei.hmos.browser\", \"MainAbility\")
停止应用
def stop_app(package_name: str, wait_time: float = 0.5)
接口说明
停止指定的应用
参数说明
使用示例
# 停止com.ohos.settingsdriver.stop_app(\"com.ohos.settings\")
- 文件拉取/推送
拉取文件
def pull_file(device_path: str, local_path: str = None, timeout: int = 60)
接口说明
从设备端的传输文件到pc端
参数说明
使用示例
# 从设备中拉取文件\"/data/local/tmp/test.log\"保存到pc端的test.logdriver.pull_file(\"/data/local/tmp/test.log\", \"test.log\")
推送文件
def push_file(local_path: str, device_path: str, timeout: int = 60)
接口说明
从pc端传输文件到设备端
参数说明
使用示例
# 从设备中推送文件test.hap保存到设备端的\"/data/local/tmp/test.hap\"driver.push_file(\"test.hap\", \"/data/local/tmp/test.hap\")
查询文件是否存在
def has_file(file_path: str) -> bool
接口说明
查询设备中是否有存在路径为file_path的文件
参数说明
使用示例
# 查询设备端是否存在文件/data/local/tmp/test_file.txtdriver.has_file(\"/data/local/tmp/test_file.txt\")
- 文本输入/清除
输入文本
def input_text(component: Union[By, UiComponent], text: str)
接口说明
向指定控件中输入文本内容
参数说明
使用示例
# 在类型为\"TextInput\"的控件中输入文本\"hello world\"driver.input_text(BY.type(\"TextInput\"), \"hello world\")
清除文本
def clear_text(component: [By, UiComponent])
接口说明
清空指定控件中的文本内容
参数说明
使用示例
# 清除类型为\"InputText\"的控件中的内容driver.clear_text(BY.type(\"InputText\"))
- 断言
Ⅰ. 常规断言
检查是否相等
def check_equal(value: Any, expect: Any = True, fail_msg: str = None, expect_equal=True)
接口说明
检查实际值和期望值相等,在不一致时抛出TestFailure异常, 并打印fail_msg
参数说明
使用示例
# 检查a等于bhost.check(a, b, \"a != b\")
检查是否超出预期
def check_greater(value: Any, expect: Any, fail_msg: str = None)
接口说明
检查value是否大于expect, 不满足时抛出TestAssertionError异常
参数说明
使用示例
# 检查a大于bhost.check_greater(a, b)
Ⅱ. 控件断言
检查控件是否存在
d ef check_component_exist(component: By, expect_exist: bool = True, wait_time: int = 0, scroll_target: Union[By, UiComponent] = None)
接口说明
检查指定UI控件是否存在
参数说明
使用示例
# 检查类型为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)
检查控件属性
def check_component(component: Union[By, UiComponent], expected_equal: bool = True, **kwargs)
接口说明
检查控件属性是否符合预期
参数说明
使用示例
# 检查id为xxx的控件的checked属性为Truedriver.check_component(BY.key(\"xxx\"), checked=True)# 检查id为check_button的按钮enabled属性为Truedriver.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)
检查图片是否存在
def check_image_exist(image_path_pc: str, expect_exist: bool = True, similarity: float = 0.95, timeout: int = 3, mode=\"template\", **kwargs)
接口说明
使用图片模板匹配算法检测当前屏幕截图中是否有指定图片,需要保证模板图片的分辨率和屏幕截图中目标图像的分辨率一致,否则会无法成功检测到目标图片
参数说明
使用示例
# 检查图片存在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算法检查图片存在, 设置最少匹配特征点数量为16driver.check_image_exist(\"test.jpeg\", mode=\"sift\", min_match_point=16)
Ⅲ. 窗口断言
检查窗口
def check_window(window: WindowFilter, title: str = None, bundle_name: str = None)
接口说明
检查指定的window的属性是否符合预期
参数说明
使用示例
# 检查当前焦点窗口的包名为com.ohos.settingdriver.check_window(WindowFilter().focused(True), bundle_name=\"com.ohos.settings\")
- 日志打印
使用以下方法在脚本或者封装的AW中打印能够记录到测试报告中的日志消息。
from devicetest.core.testcase import Step, CheckPoint, MESSAGEStep(\"点击按钮\")CheckPoint(\"检查联系人存在\")MESSAGE(\"打印一条提示消息\")
在自定义的aw实现中,可以调用driver对象中的log模块打印日志消息。
driver.log.debug(\"debug级别的日志\")driver.log.info(\"info级别的日志\")driver.log.warning(\"warning级别的日志\")driver.log.error(\"error级别的日志\")
- 读取测试项目中资源文件路径
使用以下方法在程序工作目录下resource目录以及其他工程配置文件配置的资源目录中搜索名为filename的文件。该方法会返回文件完整路径,可以仅传入文件名或者相对路径
from devicetest.utils import file_utilfile_path = file_util.get_resource_path(\"filename\")
默认搜索文件路径,设置isdir=True来读取目录路径
dir_path = file_util.get_resource_path(\"dirname\", isdir=True)
最后呢
很多开发朋友不知道需要学习那些鸿蒙技术?鸿蒙开发岗位需要掌握那些核心技术点?为此鸿蒙的开发学习必须要系统性的进行。
而网上有关鸿蒙的开发资料非常的少,假如你想学好鸿蒙的应用开发与系统底层开发。你可以参考这份资料,少走很多弯路,节省没必要的麻烦。由两位前阿里高级研发工程师联合打造的《鸿蒙NEXT星河版OpenHarmony开发文档》里面内容包含了(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战等等)鸿蒙(Harmony NEXT)技术知识点
如果你是一名Android、Java、前端等等开发人员,想要转入鸿蒙方向发展。可以直接领取这份资料辅助你的学习。下面是鸿蒙开发的学习路线图。
针对鸿蒙成长路线打造的鸿蒙学习文档。话不多说,我们直接看详细鸿蒙(OpenHarmony )手册(共计1236页)与鸿蒙(OpenHarmony )开发入门视频,帮助大家在技术的道路上更进一步。
- 《鸿蒙 (OpenHarmony)开发学习视频》
- 《鸿蒙生态应用开发V2.0白皮书》
- 《鸿蒙 (OpenHarmony)开发基础到实战手册》
- OpenHarmony北向、南向开发环境搭建
- 《鸿蒙开发基础》
- 《鸿蒙开发进阶》
- 《鸿蒙开发实战》
总结
鸿蒙—作为国家主力推送的国产操作系统。部分的高校已经取消了安卓课程,从而开设鸿蒙课程;企业纷纷跟进启动了鸿蒙研发。
并且鸿蒙是完全具备无与伦比的机遇和潜力的;预计到年底将有 5,000 款的应用完成原生鸿蒙开发,未来将会支持 50 万款的应用。那么这么多的应用需要开发,也就意味着需要有更多的鸿蒙人才。鸿蒙开发工程师也将会迎来爆发式的增长,学习鸿蒙势在必行! 自↓↓↓拿