HarmonyOS自動化測試實踐
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
一、運行環(huán)境
工具列表
- Appium-Desktop v1.8.2
- Android SDK
- Python
- Appium-Python-Client
- Pytest
- Pycharm
工具安裝:
Appium-Desktop v1.8.2
下載地址:Appium-Desktop v1.8.2, 對于鴻蒙系統(tǒng),最佳的Appium兼容版本為v1.8.2。其他版本在獲取text時的值為“0.0”
安裝過程:




Android SDK
1.下載地址:Android SDK
2.將Android Studio安裝在默認(rèn)路徑: C:\Program Files\Android\Android Studio
3.在Android Studio的SDK Manager中下載Android SDK
4.配置環(huán)境變量:
- 新建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安裝在默認(rèn)位置
環(huán)境變量配置
打開CMD,輸入 python –version 測試python命令,輸入 pip –V 測試pip命令
如果測試失敗,則需要配置python的環(huán)境變量
Appium-Python-Client
安裝命令:pip install Appium-Python-Client

Pytest
安裝命令:pip install pytest

Pycharm
1.下載地址:Pycharm
Pycharm是比較流行Python編輯器(IDE工具),選擇Community版本下載
2.將Pycharm安裝在默認(rèn)位置
3.選擇python編譯器

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

以uiautomatorviewer為例看一下常見的查找控件方式
1.通過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.通過坐標(biāo)定位,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="輸入框默認(rèn)值"]'
- 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()
鴻蒙與安卓定位方式基本相同
三、模擬用戶事件
在自動化用戶操作事件中,鴻蒙與安卓基本方式一致,以下列舉常見的幾種操作事件:
1.點擊(確認(rèn)點擊)、輸入和清空操作
- 點擊:能支持點擊跳轉(zhuǎn)的定位元素,通過.click()執(zhí)行
- driver.find_element_by_id(‘org.ohosannotations.sample:id/Id_myTextField’).click()
輸入:有輸入框需要輸入的定位元素,通過.send_keys()執(zhí)行
- driver.find_element_by_id(‘org.ohosannotations.sample:id/Id_myTextField’).send_keys(‘zdhtest’)
清空:對已輸入的輸入框清空內(nèi)容,通過.clear()執(zhí)行
- driver.find_element_by_id(‘org.ohosannotations.sample:id/Id_myTextField’).clear()
2.元素等待作用
在自動化過程中,元素出現(xiàn)受網(wǎng)絡(luò)環(huán)境,設(shè)備性能等多種因素影響。因此元素加載的時間可能不一致,從而會導(dǎo)致元素?zé)o法定位超時報錯,但是實際上元素是正常加載了的,只是出現(xiàn)時間晚一點而已, 故設(shè)置元素等待可以更加靈活的制定等待定位元素的時間,從而增強腳本的健壯性,提高執(zhí)行效率
- 強制等待: 設(shè)置固定的等待時間,使用sleep()方法即可實現(xiàn)
- from time import sleep
- #強制等待5秒
- sleep(5)
- 隱式等待: 隱式等待是針對全部元素設(shè)置的等待時間
- driver.implicitly_wait(20)
- 顯式等待: 顯式等待是針對某個元素來設(shè)置的等待時間。
方法WebDriverWait格式參數(shù)如下:
- from selenium.webdriver.support.ui import WebDriverWait
- WebDriverWait(driver, timeout, poll_frequency=0.5, ignored_exceptions=None)
- driver : WebDriver
- #timeout : 最長超時時間,默認(rèn)以秒為單位
- #poll_frequency : 休眠時間的間隔時間,默認(rèn)為0.5秒
- #ignored_exceptions : 超時后的異常信息,默認(rèn)情況下拋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)))
3.Toast內(nèi)容獲取
Toast是一種簡易的消息提示框。 當(dāng)視圖顯示給用戶,在應(yīng)用程序中顯示為浮動,和Dialog不一樣的是,它永遠(yuǎn)不會獲得焦點,無法被點擊,而且Toast顯示的時間有限,一般3秒左右就消失了,在鴻蒙應(yīng)用中,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() :方法直接保存當(dāng)前屏幕截圖到當(dāng)前腳本所在文件位置
- 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坐標(biāo)
- y1 = int(l[1] * 0.75) #起始y坐標(biāo)
- y2 = int(l[1] * 0.25) #終點y坐標(biāo)
- dr.swipe(x1, y1, x1, y2,t)
- #屏幕向下滑動
- def swipeDown(t):
- l = getSize()
- x1 = int(l[0] * 0.5) #x坐標(biāo)
- y1 = int(l[1] * 0.25) #起始y坐標(biāo)
- y2 = int(l[1] * 0.75) #終點y坐標(biāo)
- 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)
- #調(diào)用向左滑動
- swipLeft(1000)
- #調(diào)用向右滑動
- swipRight(1000)
- #調(diào)用向上滑動
- swipeUp(1000)
- #調(diào)用向下滑動
- swipeDown(1000)
6.按壓、長按、點擊(單純點擊)、移動、暫停、釋放、執(zhí)行等操作
- 強制等待: 設(shè)置固定的等待時間,使用sleep()方法即可實現(xiàn)
- 按壓:press() 開始按壓一個元素或坐標(biāo)點(x,y)。通過手指按壓手機屏幕的某個位置。 press也可以接收屏幕的坐標(biāo)(x,y)。press(self, el=None, x=None, y=None)
- TouchAction(driver).press(x=0,y=308)
- 長按:longPress() 開始按壓一個元素或坐標(biāo)點(x,y)。 相比press()方法,longPress()多了一個入?yún)?,既然長按,得有按的時間吧。duration以毫秒為單位。1000表示按一秒鐘。其用法與press()方法相同。
- long_press(self, el=None, x=None, y=None, duration=1000)
- 點擊:tap() 對一個元素或控件執(zhí)行點擊操作。用法參考press()。不能點擊跳轉(zhuǎn),單純的點擊(例如:點擊勾選、點擊點贊,點擊播放等)
- 移動:move_to() 將指針從上一個點移動到指定的元素或點。(滑動驗證條)
- move_to(self, el=None, x=None, y=None)
- 暫停:Wait()暫停腳本的執(zhí)行,單位為毫秒
- wait(self, ms=0)
- 釋放:方法release() 結(jié)束的行動取消屏幕上的指針。
- release(self)
- 執(zhí)行:perform() 執(zhí)行的操作發(fā)送到服務(wù)器的命令操作。
- perform(self)
7.獲取元素的屏幕尺寸和名稱
- 獲取屏幕尺寸:
- 屏幕總尺寸:分辨率 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
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("向輸入框輸入內(nèi)容"):
- self.text_input(annotations_locators["myTextField_控件"], "這是一條有內(nèi)涵的內(nèi)容!")
- self.ta_tap(annotations_locators["Click me_按鈕"])
- time.sleep(2)
- assert "這是一條有內(nèi)涵的內(nèi)容!" == 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("匹配結(jié)果:%s" % flag)
- assert flag is True
運行結(jié)果:
2.示例二
以Sensey組件為例,演示多點觸控的使用方法,驗證Sensey組件的雙指檢測功能
- @allure.story('Sensey')
- @allure.title("sensey_006 雙指檢測")
- @allure.tag("L1")
- @allure.severity("normal") # blocker:阻塞缺陷 critical:嚴(yán)重缺陷 normal:一般缺陷 minor:次要缺陷 trivial:輕微缺陷
- @allure.description("雙指檢測")
- @pytest.mark.flaky(reruns=1, reruns_delay=5) # reruns:重試次數(shù) 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", "雙指檢測不正確"
運行結(jié)果:
五、測試報告生成
使用allure-pytest插件
- @allure.feature('設(shè)置應(yīng)用') # 功能名稱
- class TestSettings(BaseCase):
- @allure.story('聲音和振動') # 子功能名稱
- @allure.title('settings_001 設(shè)置來電鈴聲') # 用例標(biāo)題
- @allure.severity('normal') # 缺陷級別
- @allure.description('檢查是否可以設(shè)置來電鈴聲') # 用例描述
- def test_settings_001(self):
- # 測試步驟
- with allure.step('進入聲音和振動'):
- pass
- with allure.step('設(shè)置來電鈴聲'):
- pass
- with allure.step('斷言來電鈴聲設(shè)置成功'):
- pass
生成html報告和打開
- if __name__ == '__main__':
- now = time.strftime('%Y%m%d%H%M%S', time.localtime())
- print('執(zhí)行腳本(%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)
生成報告如下:

展開詳情:

51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)