一、运行环境
工具列表
- 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
1.下载地址:Android SDK
2.将Android Studio安装在默认路径: C:\Program Files\Android\Android Studio
3.在Android Studio的SDK Manager中下载Android SDK
4.配置环境变量:
- 新建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
1.下载地址:Pycharm
Pycharm是比较流行Python编辑器(IDE工具),选择Community版本下载
2.将Pycharm安装在默认位置
3.选择python编译器

二、元素定位
元素定位工具常用的有:uiautomatorviewer.bat,weditor.exe,Appium-Desktop

以uiautomatorviewer为例看一下常见的查找控件方式
1.通过id定位,resrouce-id
element = conf.driver.find_element_by_id("Id_myButton").click()
- 1.
2.通过ClassName定位: classname
element = conf.driver.find_elements_by_class_name("android.widget.Button").click()
- 1.
3.通过Accessiblityld定位:content-desc
element = driver.find_element_by_accessibility_id("content-desc-text").click()
- 1.
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()
- 1.
- 2.
5.通过坐标定位,XY
TouchAction(conf.driver).tap(x=int(x * phonewidth), y=int(y * phoneheight)).release().perform()
- 1.
6.通过xpath定位终极定位,方式有很多种,以下列几种常见:
- 如果元素text是唯一的,可以通过text文本定位:
//*[@text=’text文本属性’]
element = conf.driver.find_element_by_xpath("//*[@text='Click me!']").click()
- 1.
- 2.
- 如果元素id是唯一的,也可以id属性定位
//*[@resource-id=’id属性’]
element = conf.driver.find_element_by_xpath("//*[@resource-id='org.ohosannotations.sample:id/Id_myButton']").click()
- 1.
- 2.
- 通过content-desc属性定位
//*[@content-desc=’desc的文本’]
element = conf.driver.find_element_by_xpath("//*[@content-desc='desc的文本']").click()
- 1.
- 2.
- contains模糊定位
//[contains(@content-desc, ‘desc的文本’)]
element = conf.driver.find_element_by_xpath("//*[contains(@content-desc, 'desc的文本')]").click()
- 1.
- 2.
- 组合定位
如果一个元素有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()
- 1.
- 2.
通过text和index属性 定位按钮Start list ability !
desc_class = '//*[@text="Start list ability !" and @index="3"]'
element = conf.driver.find_element_by_xpath(desc_class).click()
- 1.
- 2.
通过class和text属性 定位输入框
class_text = '//android.widget.EditText[@text="输入框默认值"]'
element = conf.driver.find_element_by_xpath(class_text).send_keys("zdhtest")
- 1.
- 2.
通过class和desc 定位搜索框
id_desc = '//*[contains(@resource-id, "Id_myTextField") and @content-desc="desc的文本"]'
element = conf.driver.find_element_by_xpath(id_desc).click()
- 1.
- 2.
鸿蒙与安卓定位方式基本相同
三、模拟用户事件
在自动化用户操作事件中,鸿蒙与安卓基本方式一致,以下列举常见的几种操作事件:
1.点击(确认点击)、输入和清空操作
- 点击:能支持点击跳转的定位元素,通过.click()执行
driver.find_element_by_id(‘org.ohosannotations.sample:id/Id_myTextField’).click()
- 1.
输入:有输入框需要输入的定位元素,通过.send_keys()执行
driver.find_element_by_id(‘org.ohosannotations.sample:id/Id_myTextField’).send_keys(‘zdhtest’)
- 1.
清空:对已输入的输入框清空内容,通过.clear()执行
driver.find_element_by_id(‘org.ohosannotations.sample:id/Id_myTextField’).clear()
- 1.
2.元素等待作用
在自动化过程中,元素出现受网络环境,设备性能等多种因素影响。因此元素加载的时间可能不一致,从而会导致元素无法定位超时报错,但是实际上元素是正常加载了的,只是出现时间晚一点而已, 故设置元素等待可以更加灵活的制定等待定位元素的时间,从而增强脚本的健壮性,提高执行效率
- 强制等待: 设置固定的等待时间,使用sleep()方法即可实现
from time import sleep
#强制等待5秒
sleep(5)
- 1.
- 2.
- 3.
- 隐式等待: 隐式等待是针对全部元素设置的等待时间
driver.implicitly_wait(20)
- 1.
- 显式等待: 显式等待是针对某个元素来设置的等待时间。
方法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)))
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
3.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)
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
4.截图操作
save_screenshot() :方法直接保存当前屏幕截图到当前脚本所在文件位置
driver.save_screenshot(‘aa.png’)
get_screenshot_as_file(self, filename) :将截图保留到指定文件路径
driver.get_screenshot_as_file(’./images/aa.png’)
- 1.
- 2.
- 3.
- 4.
- 5.
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)
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
6.按压、长按、点击(单纯点击)、移动、暂停、释放、执行等操作
- 强制等待: 设置固定的等待时间,使用sleep()方法即可实现
- 按压:press() 开始按压一个元素或坐标点(x,y)。通过手指按压手机屏幕的某个位置。 press也可以接收屏幕的坐标(x,y)。press(self, el=None, x=None, y=None)
TouchAction(driver).press(x=0,y=308)
- 1.
- 长按:longPress() 开始按压一个元素或坐标点(x,y)。 相比press()方法,longPress()多了一个入参,既然长按,得有按的时间吧。duration以毫秒为单位。1000表示按一秒钟。其用法与press()方法相同。
long_press(self, el=None, x=None, y=None, duration=1000)
- 1.
- 点击:tap() 对一个元素或控件执行点击操作。用法参考press()。不能点击跳转,单纯的点击(例如:点击勾选、点击点赞,点击播放等)
- 移动:move_to() 将指针从上一个点移动到指定的元素或点。(滑动验证条)
move_to(self, el=None, x=None, y=None)
- 1.
- 暂停:Wait()暂停脚本的执行,单位为毫秒
wait(self, ms=0)
- 1.
- 释放:方法release() 结束的行动取消屏幕上的指针。
release(self)
- 1.
- 执行:perform() 执行的操作发送到服务器的命令操作。
perform(self)
- 1.
7.获取元素的屏幕尺寸和名称
- 获取屏幕尺寸:
屏幕总尺寸:分辨率 phonesize = self.get_phone_size()
屏幕宽度:X值 phonewidth = phonesize["width"]
屏幕高度:Y值 phoneheight = phonesize["height"]
- 1.
- 2.
- 3.
- 4.
- 5.
- 获取元素的名称:
driver.find_element_by_xpath(xpath).text
driver.find_element_by_id(“org.ohosannotations.sample:id/Id_myTextField”).text
- 1.
- 2.
四、简单示例
以下是两个简单case
1.示例一
下面以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
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
运行结果:
2.示例二
以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", "双指检测不正确"
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
运行结果:
五、测试报告生成
使用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
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
生成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)
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
生成报告如下:

展开详情:
