一、运行环境
工具列表
- Appium-Desktop v1.8.2
- Android SDK
- Python
- Appium-Python-Client
- Pytest
- Pycharm
工具安装:
Appium-Desktop v1.8.2
下载地址:Appium-Desktop v1.8.2, 对于鸿蒙系统,最佳的Appium兼容版本为v1.8.2。其他版本在获取text时的值为“0.0”
安装过程:
Android SDK
下载地址:Android SDK
将Android Studio安装在默认路径: C:\Program Files\Android\Android Studio
在Android Studio的SDK Manager中下载Android SDK
配置环境变量:
- 新建JAVA_HOME,值为C:\Program Files\Android\Android Studio\jre
- 新建ANDROID_HOME,值为C:\Users\Administrator\AppData\Local\Android\Sdk
- 编辑Path, 新建%JAVA_HOME%\bin, %JAVA_HOME%\jre\bin, %ANDROID_HOME%\tools, %ANDROID_HOME%\tools\bin, %ANDROID_HOME%\platform-tools
Python
下载地址:Python
将Python安装在默认位置
环境变量配置
打开CMD,输入 python –version 测试python命令,输入 pip –V 测试pip命令
如果测试失败,则需要配置python的环境变量
Appium-Python-Client
安装命令:pip install Appium-Python-Client
Pytest
安装命令:pip install pytest
Pycharm
下载地址:Pycharm
Pycharm是比较流行Python编辑器(IDE工具),选择Community版本下载
将Pycharm安装在默认位置
选择python编译器
二、元素定位
元素定位工具常用的有:uiautomatorviewer.bat,weditor.exe,Appium-Desktop
以uiautomatorviewer为例看一下常见的查找控件方式
通过id定位,resrouce-id
- element = conf.driver.find_element_by_id("Id_myButton").click()
2.通过ClassName定位: classname
- element = conf.driver.find_elements_by_class_name("android.widget.Button").click()
3.通过Accessiblityld定位:content-desc
- element = driver.find_element_by_accessibility_id("content-desc-text").click()
4.通过AndroidUiAutomator
- ui_str = 'new UiScrollable(UiSelector().className("{}")).scrollIntoView(new UiSelector().textContains("{}"))'.format(list_id, text_contains)
- element = conf.driver.find_element_by_android_uiautomator(ui_str).click()
5.通过坐标定位,XY
- TouchAction(conf.driver).tap(x=int(x * phonewidth), y=int(y * phoneheight)).release().perform()
6.通过xpath定位终极定位,方式有很多种,以下列几种常见:
- 如果元素text是唯一的,可以通过text文本定位:
- //*[@text=’text文本属性’]
- element = conf.driver.find_element_by_xpath("//*[@text='Click me!']").click()
- 如果元素id是唯一的,也可以id属性定位
- //*[@resource-id=’id属性’]
- element = conf.driver.find_element_by_xpath("//*[@resource-id='org.ohosannotations.sample:id/Id_myButton']").click()
- 通过content-desc属性定位
- //*[@content-desc=’desc的文本’]
- element = conf.driver.find_element_by_xpath("//*[@content-desc='desc的文本']").click()
- contains模糊定位
- //[contains(@content-desc, ‘desc的文本’)]
- element = conf.driver.find_element_by_xpath("//*[contains(@content-desc, 'desc的文本')]").click()
- 组合定位
如果一个元素有2个属性,通过xpath也可以同时匹配2个属性,text, resource-id,class ,index,content-desc 这些属性都能任意组合定位
通过id和class属性 定位搜索框
- id_class = '//android.widget.EditText[@resource-id="org.ohosannotations.sample:id/Id_myTextField"]'
- element = conf.driver.find_element_by_xpath(id_class).click()
通过text和index属性 定位按钮Start list ability !
- desc_class = '//*[@text="Start list ability !" and @index="3"]'
- element = conf.driver.find_element_by_xpath(desc_class).click()
通过class和text属性 定位输入框
- class_text = '//android.widget.EditText[@text="输入框默认值"]'
- element = conf.driver.find_element_by_xpath(class_text).send_keys("zdhtest")
通过class和desc 定位搜索框
- id_desc = '//*[contains(@resource-id, "Id_myTextField") and @content-desc="desc的文本"]'
- element = conf.driver.find_element_by_xpath(id_desc).click()
鸿蒙与安卓定位方式基本相同
三、模拟用户事件
在自动化用户操作事件中,鸿蒙与安卓基本方式一致,以下列举常见的几种操作事件:
点击(确认点击)、输入和清空操作
- 点击:能支持点击跳转的定位元素,通过.click()执行
- driver.find_element_by_id(‘org.ohosannotations.sample:id/Id_myTextField’).click()
输入:有输入框需要输入的定位元素,通过.send_keys()执行
- driver.find_element_by_id(‘org.ohosannotations.sample:id/Id_myTextField’).send_keys(‘zdhtest’)
清空:对已输入的输入框清空内容,通过.clear()执行
- driver.find_element_by_id(‘org.ohosannotations.sample:id/Id_myTextField’).clear()
2.元素等待作用
在自动化过程中,元素出现受网络环境,设备性能等多种因素影响。因此元素加载的时间可能不一致,从而会导致元素无法定位超时报错,但是实际上元素是正常加载了的,只是出现时间晚一点而已, 故设置元素等待可以更加灵活的制定等待定位元素的时间,从而增强脚本的健壮性,提高执行效率
- 强制等待: 设置固定的等待时间,使用sleep()方法即可实现
- from time import sleep
- #强制等待5秒
- sleep(5)
- 隐式等待: 隐式等待是针对全部元素设置的等待时间
- driver.implicitly_wait(20)
- 显式等待: 显式等待是针对某个元素来设置的等待时间。
方法WebDriverWait格式参数如下:
-
- from selenium.webdriver.support.ui import WebDriverWait
-
- WebDriverWait(driver, timeout, poll_frequency=0.5, ignored_exceptions=None)
-
- driver : WebDriver
-
- #timeout : 最长超时时间,默认以秒为单位
-
- #poll_frequency : 休眠时间的间隔时间,默认为0.5秒
-
- #ignored_exceptions : 超时后的异常信息,默认情况下抛NoSuchElementException异常。
-
- WebDriverWait()一般和until()或until_not()方法配合使用
-
- from selenium.webdriver.support.ui import WebDriverWait
-
- WebDriverWait(conf.driver, 10).until(EC.visibility_of_element_located((By.ID, self.locator)))
Toast内容获取
Toast是一种简易的消息提示框。 当视图显示给用户,在应用程序中显示为浮动,和Dialog不一样的是,它永远不会获得焦点,无法被点击,而且Toast显示的时间有限,一般3秒左右就消失了,在鸿蒙应用中,Toast目前还捕捉不到,解决方法也是采用截图比较
- #Android获取Toast方式
- toast_message = "这是一个toast文本"
- message ='//*[@text=\'{}\']'.format(toast_message)
- #显示等待检测元素
- toast_element=WebDriverWait(driver, 5).until(EC.visibility_of_element_located((message, self.locator)))
- print(toast_element.text)
-
- #鸿蒙中Toast处理方式
- self.get_image(imagepath)
- flag = self.image_assert(assertimagepath, imagepath, imagesucess)
4.截图操作
- save_screenshot() :方法直接保存当前屏幕截图到当前脚本所在文件位置
- driver.save_screenshot(‘aa.png’)
-
- get_screenshot_as_file(self, filename) :将截图保留到指定文件路径
- driver.get_screenshot_as_file(’./images/aa.png’)
5.滑动操作
- #获得机器屏幕大小x,y
- def getSize():
- x = dr.get_window_size()['width']
- y = dr.get_window_size()['height']
- return (x, y)
-
- #屏幕向上滑动
- def swipeUp(t):
- l = getSize()
- x1 = int(l[0] * 0.5) #x坐标
- y1 = int(l[1] * 0.75) #起始y坐标
- y2 = int(l[1] * 0.25) #终点y坐标
- dr.swipe(x1, y1, x1, y2,t)
- #屏幕向下滑动
- def swipeDown(t):
- l = getSize()
- x1 = int(l[0] * 0.5) #x坐标
- y1 = int(l[1] * 0.25) #起始y坐标
- y2 = int(l[1] * 0.75) #终点y坐标
- dr.swipe(x1, y1, x1, y2,t)
- #屏幕向左滑动
- def swipLeft(t):
- l=getSize()
- x1=int(l[0]*0.75)
- y1=int(l[1]*0.5)
- x2=int(l[0]*0.05)
- dr.swipe(x1,y1,x2,y1,t)
- #屏幕向右滑动
- def swipRight(t):
- l=getSize()
- x1=int(l[0]*0.05)
- y1=int(l[1]*0.5)
- x2=int(l[0]*0.75)
- dr.swipe(x1,y1,x2,y1,t)
- #调用向左滑动
- swipLeft(1000)
- #调用向右滑动
- swipRight(1000)
- #调用向上滑动
- swipeUp(1000)
- #调用向下滑动
- swipeDown(1000)
6.按压、长按、点击(单纯点击)、移动、暂停、释放、执行等操作
- 强制等待: 设置固定的等待时间,使用sleep()方法即可实现
- 按压:press() 开始按压一个元素或坐标点(x,y)。通过手指按压手机屏幕的某个位置。 press也可以接收屏幕的坐标(x,y)。press(self, el=None, x=None, y=None)
- TouchAction(driver).press(x=0,y=308)
- 长按:longPress() 开始按压一个元素或坐标点(x,y)。 相比press()方法,longPress()多了一个入参,既然长按,得有按的时间吧。duration以毫秒为单位。1000表示按一秒钟。其用法与press()方法相同。
- long_press(self, el=None, x=None, y=None, duration=1000)
- 点击:tap() 对一个元素或控件执行点击操作。用法参考press()。不能点击跳转,单纯的点击(例如:点击勾选、点击点赞,点击播放等)
- 移动:move_to() 将指针从上一个点移动到指定的元素或点。(滑动验证条)
- move_to(self, el=None, x=None, y=None)
- 暂停:Wait()暂停脚本的执行,单位为毫秒
- wait(self, ms=0)
- 释放:方法release() 结束的行动取消屏幕上的指针。
- release(self)
- 执行:perform() 执行的操作发送到服务器的命令操作。
- perform(self)
获取元素的屏幕尺寸和名称
- 获取屏幕尺寸:
- 屏幕总尺寸:分辨率 phonesize = self.get_phone_size()
-
- 屏幕宽度:X值 phonewidth = phonesize["width"]
-
- 屏幕高度:Y值 phoneheight = phonesize["height"]
- 获取元素的名称:
- driver.find_element_by_xpath(xpath).text
- driver.find_element_by_id(“org.ohosannotations.sample:id/Id_myTextField”).text
四、简单示例
以下是两个简单case
示例一
下面以ohosannotations组件为例,case中包含事件有:点击、长按、输入、文本断言和图片对比断言
- def test_ohosannotations(self, getlocator):
- annotations_locators = getlocator["ohosannotations"]
- with allure.step("点击Click me_按钮的点击事件"):
- self.ta_tap(annotations_locators["Click me_按钮"])
- with allure.step("向输入框输入内容"):
- self.text_input(annotations_locators["myTextField_控件"], "这是一条有内涵的内容!")
- self.ta_tap(annotations_locators["Click me_按钮"])
- time.sleep(2)
- assert "这是一条有内涵的内容!" == self.get_element_text(annotations_locators["myText_控件"]), "文本显示不正确"
- with allure.step("长按Start extra ability, long click !控件"):
- logger.info("长按Start extra ability, long click !控件")
- self.ta_longpress(annotations_locators["Start extra ability, long click_按钮"], 1000)
- time.sleep(2)
- with allure.step("断言弹出框图片与期望图片是否一致"):
- self.get_image(imagepath)
- flag = self.image_assert(assertimagepath, imagepath, imagesucess)
- if flag is True:
- with open(imagesucess, "rb") as f:
- context = f.read()
- allure.attach(context, "匹配成功的图片", attachment_type=allure.attachment_type.PNG)
- else:
- with open(imagepath, "rb") as f:
- context = f.read()
- allure.attach(context, "匹配失败的图片", attachment_type=allure.attachment_type.PNG)
- logger.info("匹配结果:%s" % flag)
- assert flag is True
运行结果:
示例二
以Sensey组件为例,演示多点触控的使用方法,验证Sensey组件的双指检测功能
- @allure.story('Sensey')
- @allure.title("sensey_006 双指检测")
- @allure.tag("L1")
- @allure.severity("normal") # blocker:阻塞缺陷 critical:严重缺陷 normal:一般缺陷 minor:次要缺陷 trivial:轻微缺陷
- @allure.description("双指检测")
- @pytest.mark.flaky(reruns=1, reruns_delay=5) # reruns:重试次数 reruns_delay:重试的间隔时间
- def test_sensey_006(self, getlocator):
- logger.info("sensey_006 双指检测")
- self.ta_tap(["TOUCH DETECTOR", "XPATH", "//*[@text=\"TOUCH DETECTOR\"]"])
- self.ta_tap(["Touch Detection", "XPATH", "//*[@resource-id=\"Id_switch_touch\"]"])
- a1 = TouchAction(conf.driver).tap(x=530, y=1380).release()
- a2 = TouchAction(conf.driver).tap(x=730, y=1380).release()
- action = MultiAction(conf.driver)
- action.add(a1,a2)
- action.perform()
- time.sleep(0.5)
- text = self.get_element_text(["Result", "XPATH", "//*[@resource-id=\"Id_tv_result\"]"])
- assert text == "Two Finger Tap", "双指检测不正确"
运行结果:
五、测试报告生成
使用allure-pytest插件
- @allure.feature('设置应用') # 功能名称
- class TestSettings(BaseCase):
-
- @allure.story('声音和振动') # 子功能名称
- @allure.title('settings_001 设置来电铃声') # 用例
- @allure.severity('normal') # 缺陷级别
- @allure.description('检查是否可以设置来电铃声') # 用例描述
- def test_settings_001(self):
- # 测试步骤
- with allure.step('进入声音和振动'):
- pass
- with allure.step('设置来电铃声'):
- pass
- with allure.step('断言来电铃声设置成功'):
- pass
生成html报告和打开
- if __name__ == '__main__':
-
- now = time.strftime('%Y%m%d%H%M%S', time.localtime())
- print('执行脚本(%s)' % now)
-
- xml_path = './reports/report-%s/xml' % now
- html_path = './reports/report-%s/html' % now
- case_path = './testcases/'
-
- # 运行测试脚本
- pytest.main(['-s', '-q', '--alluredir', xml_path, case_path])
-
- # 生成html报告
- cmd = 'allure generate %s -o %s --clean' % (xml_path, html_path)
- os.system(cmd)
-
- # 打开测试报告
- cmd = 'allure open %s' % html_path
- os.system(cmd)
生成报告如下:
展开详情: