Appium iOS 自动化测试总结
一、iOS Appium 原理
1.1 iOS 9.3 系統(tǒng)之前自動(dòng)化測(cè)試
1.1.1 Native 自動(dòng)化
這是iOS 9.3 系統(tǒng)之前自動(dòng)化測(cè)試的架構(gòu)模式。通過Android Appium 原理的學(xué)習(xí) ,我們很容易理解 iOS Appium 原理:
-
Appium Client 端執(zhí)行代碼發(fā)送到 Appium Server端(Server 集成了蘋果官方的 Instruments);
-
Server 端將一行行代碼翻譯成一條條指令,同時(shí)在手機(jī)上注入 bootstrap.jar ;
-
Server 與該 jar 包通信將指令傳給 bootstrap.jar,jar 包調(diào)用手機(jī)里的自動(dòng)化測(cè)試框架(UIAutomation),UIAutomation框架執(zhí)行指令。
1.1.2 Hybrid(WebView)自動(dòng)化
通過 Android Appium 原理的學(xué)習(xí),Android 4.4 系統(tǒng)之后,Appium 支持使用 ChromeDriver 進(jìn)行對(duì) Hybrid 頁面的自動(dòng)化測(cè)試。那么 iOS 上是怎么做的呢?
iOS 上早期蘋果官方就一直提供 iOS webkit debug proxy(這是蘋果官方自己開發(fā)的私有的通信協(xié)議),Appium 集成了該框架,通過它傳遞指令。
1.2 iOS 9.3 系統(tǒng)之后自動(dòng)化測(cè)試
Appium 在 iOS 下工具的變革
- iOS 9 之前一直以 instruments 下的 UIAutomation為驅(qū)動(dòng)底層技術(shù)(弊端由于 instruments 的限制,單臺(tái) mac 只能對(duì)應(yīng)單臺(tái)設(shè)備);
- iOS 9.3 時(shí)代推出 XCUITest 工具,用以替代 UIAutomation;
- iOS 10 時(shí)代蘋果直接廢棄了 UIAutomation、Facebook 推出 WebDriverAgent(實(shí)現(xiàn)的 server 能夠支持單臺(tái) mac 對(duì)應(yīng)多個(gè)設(shè)備);
- Appium 在iOS 9.3 后全面采用 WebDriverAgent 的方案。
1.2.1 關(guān)于 WebDriverAgent
- FaceBook 出品;
- 實(shí)現(xiàn)了一個(gè) server,通過 server 可以遠(yuǎn)程控制 iOS 設(shè)備:啟動(dòng)應(yīng)用、關(guān)閉應(yīng)用、點(diǎn)擊、滾動(dòng)等操作;
- 通過連接 XCTest.framework 調(diào)用蘋果的 API 執(zhí)行動(dòng)作;
- 支持多個(gè)設(shè)備同時(shí)進(jìn)行自動(dòng)化;
- Appium、Macaca 已經(jīng)集成。
但是 WebDriverAgent 僅僅只提供了一個(gè) server(和 inspect 進(jìn)行元素定位),并沒有像 Appium 一樣提供 java 或 python 的 Client 端去寫腳本,腳本執(zhí)行的時(shí)候發(fā)送指令給 server,然后去運(yùn)行。WebDriverAgent 要求你自己去實(shí)現(xiàn) Client 端,即拿 Java/ Python 的 WebDriver 庫(kù)進(jìn)行封裝,然后發(fā)送指令。 所以 WebDriverAgent 其實(shí)就類似于 Appium server,就只是一個(gè) server。
1.2.2 關(guān)于 iOS 9.3 之后的 Appium 自動(dòng)化架構(gòu)模式
Appium 很粗暴的把整個(gè) WebDriverAgent 直接集成到自己的項(xiàng)目里,然后通信機(jī)制就走 WebDriverAgent,Appium 其實(shí)就提供了一個(gè) Client 端的作用。
所以 iOS 9.3 系統(tǒng)之后自動(dòng)化測(cè)試核心是 WebDriverAgent,Appium 就提供了一個(gè) Client 端來寫腳本和發(fā)送指令。
通過前面的學(xué)習(xí),我們知道 Appium 自動(dòng)化架構(gòu)模式可以用一個(gè)抽象的架構(gòu)表示,就是下面這樣的:
iOS 9.3以及之后的 Appium 自動(dòng)化架構(gòu)模式如下圖所示:
從圖中可以看出:
-
Client 端是 Appium 之前本身提供的;
-
Server 端是:WebDriverAgent 和 Instruments;( Appium 直接把 WebDriverAgent 整個(gè)集成進(jìn)來,Instruments 是為了支持 iOS 9.3 之前的系統(tǒng))
-
最右邊是一個(gè)手機(jī)
- 之前 Server 是和 bootstrap.jar 通信,這里 WebDriverAgent 提供了 WebDriverAgentRunner (類似 bootstrap.jar 的功能),WebDriverAgent與之通信;
- WebDriverAgentRunner 是一個(gè)應(yīng)用,Client 和 server 運(yùn)行了之后,WebDriverAgentRunner 會(huì)被裝到手機(jī)上,這個(gè)應(yīng)用會(huì)接收來自 Server 的指令,并連接底層的 XCTest.framwork,并告訴 XCTest.framwork 操作手機(jī)進(jìn)行自動(dòng)化。
1.2.3 必裝的軟件
Xcode、command line tool、libimobiledevice、ios-deploy、carthage、WebDriverAgent、Appium。
底層用到的工具之一,用于獲取 iOS 設(shè)備信息。
其常用命令如下:
-
查看當(dāng)前所連接的設(shè)備
idevice_id -l # 顯示當(dāng)前所連接設(shè)備的 udid instruments -s devices # 列出所有設(shè)備,包括真機(jī)、模擬器、mac -
安裝應(yīng)用
ideviceinstaller -u [udid] -i [xxx.ipa] # xxx.ipa 為應(yīng)用在本地的路徑 -
卸載應(yīng)用
ideviceinstaller -u [udid] -U [bundleId] -
查看設(shè)備已安裝的應(yīng)用
ideviceinstaller -u [udid] -l # 查看設(shè)備安裝的第三方應(yīng)用 ideviceinstaller -u [udid] -l -o list_user # 同上,查看設(shè)備安裝的第三方應(yīng)用 ideviceinstaller -u [udid] -l -o list_system # 查看設(shè)備安裝的系統(tǒng)應(yīng)用 ideviceinstaller -u [udid] -l -o list_all # 查看設(shè)備安裝的所有應(yīng)用 -
獲取設(shè)備信息
- 其他系統(tǒng)文件信息
ios-deploy、ideviceinstaller 類似 android 的 adb;
authroize-ios,iOS 授權(quán)工具,主要用于模擬器中一些權(quán)限的授權(quán);
1.2.4 安裝 WebDriverAgent
從 FB 的 GitHub 上下載WebDriverAgent
GitHub 上下載WebDriverAgent
初始化項(xiàng)目
在 WebDriverAgent 目錄下執(zhí)行:./Scripts/bootstrap.sh
編譯 WebDriverAgent
a. open WebDriverAgent.xcodeproj(會(huì)使用默認(rèn)打開工具Xcode打開項(xiàng)目)。b. 修改 WebDriverAgent.lib 以及 WebDriverAgentRunner 這兩個(gè) target 下的 General 和 Build Settings列表(前者是在 mac 上運(yùn)行的,后者是在手機(jī)上運(yùn)行的)。c. General 列表需要修改:簽名 Signing 和 BundleId: 簽名 Signing:可以用個(gè)人免費(fèi)開發(fā)者證書,用任意 AppleId 可申請(qǐng);BundleId:之前 BundleId 是綁定了 FB team 的證書的,不能使用,所以要改一個(gè)新的。 Build Settings列表需要修改 BundleId 和 上一步一樣。d. WebDriverAgentRunner 是在手機(jī)上運(yùn)行的,要想在手機(jī)上安裝,需要和上一步一樣修改 General 和 Build Settings 列表。e. 手機(jī)上設(shè)置 - 通用 - 描述文件 里信任一下該證書。f. Xcode - Product - Test,會(huì)安裝 WebDriverAgentRunner 并啟動(dòng) WebDriverAgent 這個(gè) server。替換 Appium 下的 WebDriverAgent:刪除原 WebDriverAgent 文件夾,把編譯好的 WebDriverAgent 放進(jìn)去即可
a . 如果用 npm 安裝的目錄在:cd /usr/local/lib/node_modules/appium/node_modules/appium-xcuitest-driver/WebDriverAgent/;b. 如果用 desktop 安裝的目錄在:/Applications/Appium.app/Contents/Resources/app/node_modules/appium/node_modules/appium-xcuitest-driver/WebDriverAgent/)1.2.5 開始跑腳本 Sample-Code
1.2.5.1 準(zhǔn)備 APP
這里我們需要將TestApp重新編譯才能使用。
進(jìn)入 APP 其 xxx.xcodeproj 對(duì)應(yīng)的目錄,open WebDriverAgent.xcodeproj 打開項(xiàng)目;
修改該項(xiàng)目里 target 的 General 和 Build Settings列表(和上面一樣);
通過 Xcode 編譯運(yùn)行。
或者通過 xcodebuild 命令通過命令行編譯運(yùn)行 xcodebuild -project TestApp.xcodeproj -target TestApp -sdk iphoneos10.3 -configurationdevelopment1.2.5.2 準(zhǔn)備腳本
-
iOS 項(xiàng)目的 Desired_caps
Desired_caps:{’platformName’:’iOS’,‘platformVersion’:’10.3.3’,‘devideName’:’iPhone 7 Plus’,‘udid’:’’, #如果是真機(jī)的話必須提供‘a(chǎn)pp’:’~/appPath/app.app’, #app路徑,如果只填bundleId,那就是通過id啟動(dòng)已有的App} -
iOS 的元素定位
Appium Desktop - Inspect (推薦)WebDriverAgent - Inspector
這里說說 Appium Desktop - Inspect 是怎么使用的。
-打開并啟動(dòng) Appium Desktop,點(diǎn)擊軟件右上角第一個(gè)按鈕 “ start inspector session”;
-會(huì)出現(xiàn)彈窗,彈窗里配置好 Desired_caps。
- 定位方式推薦:AccessbilityId
其他Desired_caps配置參見官方文檔:https://github.com/appium/appium/blob/master/docs/en/writing-running-appium/caps.md
注意
webdriver.py 里只延展定義了針對(duì)移動(dòng)端的 API,is_displayed()、.is_enabled()() 等這些 API 可以去看 WebDriver 即 Slenium 2 本身的。
WebDriver 本身的 API 詳見
原文鏈接:https://testerhome.com/topics/10068
1.3 IOS元素定位方法簡(jiǎn)單介紹
由于使用id、className、AccessibilityId定位方式較為簡(jiǎn)單,多數(shù)情況下,在同一個(gè)頁面,都不是唯一存在的,不能識(shí)別一個(gè)元素。而 xpath定位方式在 xcui 底層原生不支持,由 appium 額外支持的,定位速度很慢,而且有時(shí)候定位不到元素的情況存在。綜上所述,在 iOS 的 UI 自動(dòng)化中,使用原生支持的iOSNsPredicateString定位方式是最好,支持也是最好的。
1.3.1 定位方式
iOS 版本全支持,底層測(cè)試框架無論是XCUITest 或 UIAutomation,可支持元素的單個(gè)屬性和多個(gè)屬性定位,推薦使用。一個(gè)元素有這些屬性:type、value、name、label、enabled、visible,有些元素的屬性只有以上的部分屬性,如下圖所示,可根據(jù)這些屬性進(jìn)行元素定位
1.3.2 元素屬性的介紹
type:元素類型,與className作用一致,如:XCUIElementTypeStaticText
value: 一般不用
name:元素的文本內(nèi)容,可用作 AccessibilityId定位方式,如:測(cè)試420班級(jí)群
label:絕大多數(shù)情況下,與 name 作用一致
enabled:元素是否可點(diǎn)擊,一般值為true或者false
visible:元素是否可見,一般值為true或者false
1.3.3 元素定位方式
元素的定位方式都是一個(gè)屬性+運(yùn)算符+值形式存在
1、比較運(yùn)算符:>,<,==,>=,<=,!=
可用于數(shù)值和字符串的比較,
如:name>100 或name == ‘測(cè)試’
2、范圍運(yùn)算符:IN,BETWEEN
可用于數(shù)值和字符串的范圍核對(duì)
如:name BETWEEN {3,10},name IN {‘Alan’,‘May’}
3、字符串相關(guān):CONTAINS、BEGINSWITH、ENDSWITH
包含某個(gè)字符串,如:label CONTAINS ‘測(cè)試’
以某個(gè)字符串開頭,如:label BEGINSWITH ‘420’
以某個(gè)字符串結(jié)束,如:label ENDSWITH ‘班級(jí)群’
PS:在三個(gè)關(guān)鍵字后加上[c]不區(qū)分大小寫,可用于字母的校驗(yàn);[d]不區(qū)分發(fā)音符號(hào),即沒有重音符號(hào)($、#、%等);[cd]即不區(qū)分大小寫,也不區(qū)分發(fā)音符號(hào),如:name CONTAINS[c] ABcd和name CONTAINS abcd、name CONTAINS ABCD是等同的,注意后面兩個(gè)沒帶[c]的不相等
4、通配符:LIKE
通配符也接受[cd],?代表一個(gè)字符,*代表多個(gè)字符
如:一個(gè)元素的label屬性為
label LIKE ‘420測(cè)試班級(jí)群’
label LIKE ‘420測(cè)?班級(jí)群’
label LIKE ‘420??班級(jí)群’
label LIKE ‘42?測(cè)試班?群’
label LIKE ‘*試班級(jí)群’
label LIKE ‘420測(cè)試班*’
label LIKE ‘42*級(jí)群’
label LIKE ‘4試群’
以上這么多種文本都可以被識(shí)別為同一個(gè)元素。
5、正則表達(dá)式:MATCHES
如:以4開頭,以群結(jié)束,
label MATCHES ‘^4.+群$’
1.3.4 以一種屬性定位元素
可以用元素的屬性:type、value、name、label、enabled、visible,進(jìn)行定位:
type == XCUIElementTypeStaticText,
label CONTAINS ‘測(cè)試’
label LIKE ‘*試班級(jí)群’
enabled == true
visible == false
1.3.5 以兩種或兩種以上屬性定位元素
就是以上單個(gè)屬性定位用符號(hào)AND連接起來即可。如:
type == XCUIElementTypeStaticText AND labelCONTAINS '測(cè)試
type == XCUIElementTypeStaticText AND labelCONTAINS ‘測(cè)試’ AND enabled == true
1.3.6 父子關(guān)系、兄弟關(guān)系定位
#父子關(guān)系定位 self.driver.find_element_by_android_uiautomator('new UiSelector().resourceId("com.xueqiu.android:id/title_container").childSelector(text("股票"))')#兄弟關(guān)系定位 self.driver.find_element_by_android_uiautomator('new UiSelector().resourceId("com.xueqiu.android:id/title_container").fromParent(text("股票"))')1.3.7 滾動(dòng)查找
#滾動(dòng)查找 self.driver.find_element_by_android_uiautomator('new UiScrollable(new UiSelector().scrollable(true).instance(0)).scrollIntoView(new UiSelector().text("查找的元素文本").instance(0));')1.3.8 使用方法
// 等于
MobileElement photo = driver.findElementByIosNsPredicate("name= ‘head new‘");// 模糊匹配
MobileElement photo =driver.findElementByIosNsPredicate("name LIKE ‘*new‘");// 正則表達(dá)式匹配
MobileElement photo = driver.findElementByIosNsPredicate("nameMATCHES ‘^$‘");// 包含
List<IOSElement> items1 = driver.findElementsByIosNsPredicate("nameCONTAINS ‘我的‘");// 以"我的"開始
List<IOSElement> items2 = driver.findElementsByIosNsPredicate("nameBEGINSWITH ‘我的‘");// 以"我的"開始并且以"消息"結(jié)尾
List<IOSElement> items3 = driver.findElementsByIosNsPredicate("nameBEGINSWITH ‘我的‘ && name ENDSWITH ‘消息‘");其中屬性名參照inspector的屬性字段,關(guān)鍵字LIKE,MATCHES,CONTAINS,BEGINSWITH,ENDSWITH必須是大寫,匹配的字符需要用單引號(hào)
小坑建議:
如果顯示在界面的文本唯一或是第一個(gè)出現(xiàn):使用accessibility
如果class唯一或是第一個(gè)出現(xiàn):className
ID或class不方便定位,控件屬性有明確的匹配規(guī)則:accessibility
如果有工具可以直接給出準(zhǔn)確的xpath:xpath
實(shí)在不行就用坐標(biāo)吧
二、Appium 搭建 IOS
可以參考:
wangmcn84作者的模擬器
真機(jī)自動(dòng)化環(huán)境搭建
真機(jī)自動(dòng)化環(huán)境搭建-實(shí)際操作
http://10.0.223.58:8100/status
如果是想查看UI的圖層,則可訪問http://localhost:8100/inspector,方便書寫測(cè)試用 已廢棄
三、定位方法性能對(duì)比
查找元素的順序,從快到慢:
ios_predicate >> accessibility_id >> class_name >>xpath(論壇比較多的說法是class_name>>accessibility_id,在這里我們姑且認(rèn)為它們的速度是一樣的。)
driver.find_element_by_ios_predicate(“value == ‘ClearEmail’”)
driver.find_element_by_ios_predicate(“type == ‘’ AND value == ‘’)
https://www.jianshu.com/p/a6c2d72fe704
替代以前的name定位方式,在 iOS 上,主要使用元素的label或name(兩個(gè)屬性的值都一樣)屬性進(jìn)行定位,如該屬性為空,也是不能使用該屬性。
driver.find_element_by_accessibility_id(‘ClearEmail’)
使用元素的type屬性定位,特別注意該屬性的唯一性!class_name唯一的情況并不多,一般情況下用不上。
driver.find_element_by_class_name(‘XCUIElementTypeButton’)
由于 iOS 10開始使用的 XCUITest 框架原生不支持,定位速度很慢,所以官方現(xiàn)在不推薦大家使用,也有其他替代的定位方式可使用。
1)使用絕對(duì)路徑定位:
driver.find_element_by_xpath(’/XCUIElementTypeApplication/XCUIElementTypeButton’)
2)使用相對(duì)路徑定位
driver.find_element_by_xpath(’//XCUIElementTypeButton’)
3)通過元素的索引定位
driver.find_element_by_xpath(’//XCUIElementTypeButton[index]’)
4)通過元素的屬性定位
一種屬性:
driver.find_element_by_xpath(”//className[@value=‘ClearEmail’]“)
兩種屬性:
driver.find_element_by_xpath(”//className[@value=‘ClearEmail’][@ visible =true]“)
部分屬性(最強(qiáng)大):driver.find_element_by_xpath(”//className[contains(@value,‘ClearEmail’)]")
5)iOSNsPredicateString
僅支持 iOS 10或以上,可支持元素的單個(gè)屬性和多個(gè)屬性定位,推薦使用。
一種屬性:MobileBy.iOSNsPredicateString(“type == ‘XCUIElementTypeButton’”)
兩種屬性:MobileBy.iOSNsPredicateString(“type == ‘XCUIElementTypeButton’ AND label == ‘更多信息’”)
四、經(jīng)典問題
appium測(cè)試-操作Android非原生View(自定義)控件(如部分日期和地點(diǎn)選擇下拉控件),基于JavaCV的圖像匹配方法
Appium使用swipe定位滾動(dòng)列表和滾動(dòng)屏幕元素
iOS 測(cè)試 ios+appium 自動(dòng)化 click 無效
問題現(xiàn)象
app 頁面上可以看到元素,并且通過 find_element_by_xpath 方法也可以定位到,但操作 click 后,頁面并未如預(yù)期進(jìn)行跳轉(zhuǎn),點(diǎn)擊實(shí)際上沒有生效
問題原因
通過 self.driver.page_source 獲取頁面 xml,發(fā)現(xiàn)元素屬性 visible=‘false’,導(dǎo)致 click 不生效
解決方法
用 TouchAction 方法,通過坐標(biāo)進(jìn)行點(diǎn)擊,python 實(shí)現(xiàn)如下
思路:如果控件的屬性visible是false的話,請(qǐng)使用控件坐標(biāo)并獲取中心點(diǎn)用tap點(diǎn)擊,直接點(diǎn)擊控件會(huì)失敗
需要引入 TouchAction 庫(kù)
from appium.webdriver.common.touch_action import TouchAction元素錯(cuò)位 卸載騰訊tbs
IOS原生定位不支持純數(shù)字匹配
五、補(bǔ)充
目前,Appium 支持的定位方式,如下所示:
cssSelector # Selenium 最強(qiáng)大的定位方法,比 xpath 速度快,但比 xpath 難上手 linkText # 鏈接元素的全部顯示文字 partialLinkText # 鏈接元素的部分顯示文字 name # 元素的 name 屬性,目前官方在移動(dòng)端去掉這個(gè)定位方式,使用 AccessibilityId 替代 tagName # 元素的標(biāo)簽名 className # 元素的 class 屬性 id # 元素的 id 屬性 xpath # 比 css 定位方式稍弱一些的定位方法,但勝在容易上手,比較好使用,缺點(diǎn)就是速度慢一些。 AccessibilityId # Appium 中用于替代 name 定位方式 AndroidUIAutomator # Android 測(cè)試,最強(qiáng)大速度最快的定位方式 iOSNsPredicateString # iOS 謂詞的定位方式,僅支持 XCTest 框架,需大于 iOS 9.3或以上 IosUIAutomation # iOS 謂詞的定位方式,僅支持 UIAutomation 框架,需大于 iOS 9.3或以下 iOSClassChain # 國(guó)外大神 Mykola Mokhnach 開發(fā)類似 xpath 的定位方式,僅支持 XCTest 框架,,不如 xpath 和 iOSNsPredicateString 好 windowsAutomation # windows 應(yīng)用自動(dòng)化的定位方式詳細(xì)可以參考:https://zhuanlan.zhihu.com/p/28625273
總結(jié)
以上是生活随笔為你收集整理的Appium iOS 自动化测试总结的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: springboot测试类无法注入bea
- 下一篇: MYsql源码及其剖析