轉載:http://debugtalk.com/post/build-app-automated-test-platform-from-0-to-1-Appium-interrogate-iOS-UI/node
前兩天微信忽然發來一條系統消息,提示DebugTalk
能夠開通原創標識了(同時也有了評論功能),雖然一直在期待,但沒想到來得這麼快,着實是個不小的驚喜。android
另外,最近在公衆號後臺也收到好幾個朋友的信息,有的是詢問某某部分何時能發佈,有的是但願能加快更新速度。說實話,收到這樣的信息雖然會有壓力,但真的挺開心的,由於這說明DebugTalk
至少能給一部分人帶去價值,這說明這件事自己仍是值得堅持去作的。ios
不過,在更新頻率這件事兒上,的確是要跟你們說抱歉了。由於DebugTalk
發佈的內容全都是原創,主題基本上都是來源於我平常測試工做的經驗積累,或者我近期學習一些測試技術的收穫總結,這也意味着,我寫的東西不少時候並非本身徹底熟悉的(徹底掌握的東西也沒有足夠的動力專門花時間去寫)。ruby
就拿最近連載的《從0到1搭建移動App功能自動化測試平臺》系列來講,因爲我也是邊探索邊總結,所以中途不免會遇到一些意想不到的坑,形成額外的耗時,並且爲了保證文章能儘可能通俗易通,我也須要對涉及到的內容充分進行理解,而且通過大量實踐進行驗證,而後才能站在半個初學者、半個過來人的角度,從新整理思路,最後以儘量流暢的思路將主題內容講解清楚。微信
基於這些緣由,DebugTalk
要作到每日更新是很難了,可是保證每週發佈1~2篇仍是能夠的,但願你們能理解。app
在上一篇文章中,咱們成功地經過Appium Inspector調用模擬器並運行iOS應用,iOS的自動化測試環境也已所有準備就緒了。工具
那麼接下來,咱們就能夠開始實現自動化測試了麼?post
貌似還不行。在開始以前,咱們先想下什麼是APP功能自動化測試。學習
APP的功能自動化測試,簡單地來講,就是讓功能測試用例自動地在APP上執行。具體到每個測試用例,就是能模擬用戶行爲對UI控件進行操做,自動化地實現一個功能點或者一個流程的操做。再細分到每一步,就是對UI控件進行操做。測試
所以,在正式開始編寫自動化測試用例以前,咱們還須要熟悉如何與APP的UI控件進行交互操做。
在iOS系統中,UI控件有多種類型,常見的有按鈕(UIAButton)、文本(UIAStaticText)、輸入框(UIATextField)等等。但無論是對什麼類型的UI控件進行操做,基本均可以分解爲三步,首先是獲取目標控件的屬性信息,而後是對目標控件進行定位,最後是對定位到的控件執行動做。
在Appium中,要獲取iOS的UI控件元素信息,能夠採用兩種方式:一種是在前一篇文章中提到的Appium Inspector,另外一種是藉助Ruby實現的appium_console
,在Terminal中經過命令進行查詢。
運行Appium Server,並啓動【Inspector】後,總體界面以下圖所示。
現對照着這張圖對Appium Inspector進行介紹。
在右邊部分,是啓動的模擬器,裏面運行着咱們的待測APP。咱們能夠像在真機中同樣,在模擬器中執行任意功能的操做,固然,模擬器跟真機畢竟仍是有區別的,跟傳感器相關的功能,例如攝像頭、重力感應等,是無法實現的。
在左邊部分,就是Appium Inspector
。Inspector主要由以下四個部分組成:
在實踐操做中,Inspector最大的用途就是在能夠可視化地查看UI元素信息,而且能夠將操做轉換爲腳本代碼,這對初學者尤其有用。
例如,在預覽區點擊選中按鈕「BUY NOW」,而後在UI信息展現區的Details窗口就能夠看到該按鈕的全部屬性信息。在交互操做區點擊【Tap】按鈕後,就會模擬用戶點擊「BUY NOW」按鈕,而且在腳本區域生成當次按鈕點擊的腳本(選擇Ruby語言):
1
|
find_element(
:name, "BUY NOW >").click
|
如上就是使用Appium Inspector
的通常性流程。
有了Appium Inspector
,爲何還須要Appium Ruby Console
呢?
其實,Appium Ruby Console
也並非必須的。通過與多個熟悉Appium
的前輩交流,他們也從未用過Appium Ruby Console
,這說明Appium Ruby Console
並非必須的,沒有它也不會影響咱們對Appium
的使用。
可是,這並不意味着Appium Ruby Console
是多餘的。通過這些天對Appium
的摸索,我愈加地喜歡上Appium Ruby Console
,而且使用的頻率愈來愈高,如今已基本上不多使用Appium Inspector
了。這種感受怎麼說呢?Inspector
相比於Ruby Conosle
,就像是GUI
相比於Linux Terminal
,你們應該能體會了吧。
Appium Inspector
的功能是很齊全,GUI操做也很方便,可是,最大的問題就是使用的時候很是慢,在預覽界面區切換一個頁面經常須要好幾秒,甚至數十秒,這是很難讓人接受的。
在上一節中也說到了,Inspector最大的用途就是在能夠可視化地查看UI元素信息,而且能夠將操做轉換爲腳本代碼。可是當咱們對Appium
的經常使用API熟悉之後,咱們就再也不須要由工具來生成腳本,由於本身直接寫會更快,前提是咱們能知道目標控件的屬性信息(type、name、label、value)。
在這種狀況下,若是能有一種方式能夠供咱們快速查看當前屏幕的控件屬性信息,那該有多好。
慶幸的是,在閱讀Appium
官方文檔時,發現Appium
的確是支持命令行方式的,這就是Appium Ruby Console
。
Appium Ruby Console
是採用Ruby語言開發的,在使用方式上面和Ruby的irb
很相似。
在使用Appium Ruby Console
時,虛擬機的配置信息並不會從GUI中讀取,而是要經過配置文件進行指定。
配置文件的名稱統一要求爲appium.txt
,內容形式以下所示:
1
2
3
4
5
|
[caps]
platformName = "ios"
platformVersion = '9.3',
app = "/path/to/UICatalog.app.zip"
deviceName = "iPhone Simulator"
|
其中,platformName
指定虛擬機操做系統類型,「ios」或者」android」;platformVersion
指定操做系統的版本,例如iOS的’9.3’,或者Android的’5.1’;app
指定被測應用安裝包的路徑。這三個參數是必須的,與Inspector中的配置也能對應上。
在使用Appium Ruby Console
時,首先須要啓動Appium Server
,經過GUI
或者Terminal
都可。
而後,在Terminal中,進入到appium.txt
文件所在的目錄,執行arc
命令便可啓動Appium Ruby Console
。arc
,便是appium ruby console首字母的組合。
1
2
3
4
|
➜ ls
appium.txt
➜ arc
[1] pry(main)>
|
接下來,就能夠經過執行命令查詢當前設備屏幕中的控件信息。
使用頻率最高的一個命令是page
,經過這個命令能夠查看到當前屏幕中全部控件的基本信息。
例如,當屏幕停留在前面截圖中的頁面時,執行page
命令能夠獲得以下內容。
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
43
|
[1] pry(main)> page
UIANavigationBar
name: HomeView
id: Home => Home
米 => m
去看看 => View
UIAButton
name, label: tabbar category gray
UIAImage
name: debugtalk_logo.png
UIAButton
name, label: tabbar cart gray
UIATableView
value: rows 1 to 4 of 15
UIAPageIndicator
value: page 2 of 2
UIATableCell
name: For the first time ever in a hand held camera, the Osmo brings professional, realtime cinema-quality stabilization.
id: 米 => m
UIAStaticText
name, label, value: For the first time ever in a hand held camera, the Osmo brings professional, realtime cinema-quality stabilization.
id: 米 => m
UIAStaticText
name, label, value: OSMO
UIAButton
name, label: SHOP NOW >
UIATableCell
name: Ronin
UIAStaticText
name, label, value: Ronin
UIAStaticText
name, label, value: Phantom
id: 米 => m
... (略)
UIAButton
name, label: Store
value: 1
id: 門店 => Store
... (略)
UIAButton
name, label: My Account
id: My Account => My Account
nil
|
經過返回信息,咱們就能夠看到全部控件的type、name、label、value屬性值。若是在某個控件下沒有顯示label或value,這是由於這個值爲空,咱們能夠不予理會。
因爲page
返回的信息太多,可能不便於查看,所以在使用page
命令時,也能夠指定控件的類型,至關於對當前屏幕的控件進行篩選,只返回指定類型的控件信息。
指定控件類型時,能夠經過string類型進行指定(如 page 「Image」),也可經過symbol類型進行指定(如 page :cell)。指定的類型可只填寫部份內容,而且不分區大小寫。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
[2] pry(main)> page "Image"
UIAImage
name: debugtalk_logo.png
nil
[3] pry(main)> page :cell
UIATableCell
name: DebugTalk’s smartest flying camera ever.
id: 米 => m
UIATableCell
name: Ronin
UIATableCell
name: Phantom
id: 米 => m
nil
|
若是須要查看當前屏幕的全部控件類型,能夠執行page_class
命令進行查看。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
[4] pry(main)> page_class
14x UIAButton
8x UIAStaticText
4x UIAElement
4x UIATableCell
2x UIAImage
2x UIAWindow
1x UIAPageIndicator
1x UIATableView
1x UIAStatusBar
1x UIANavigationBar
1x UIATabBar
1x UIAApplication
nil
|
基本上,page
返回的控件信息已經足夠知足絕大多數場景需求,但有時候狀況比較特殊,須要enabled
、xpath
、visible
、座標等屬性信息,這時就能夠經過執行source
命令。執行source
命令後,就能夠返回當前屏幕中全部控件的全部信息,以xml格式進行展示。
獲取到UI控件的屬性信息後,就能夠對控件進行定位了。
首先介紹下最通用的定位方式,find
。經過find
命令,能夠實如今控件的諸多屬性值(name
、label
、value
、hint
)中查找目標值。查詢時不區分大小寫,若是匹配結果有多個,則只返回第一個結果。
1
2
3
4
|
[5] pry(main)> find('osmo')
#<Selenium::WebDriver::Element:0x..febd52a30dcdfea32 id="2">
[6] pry(main)> find('osmo').label
"Osmo"
|
另外一個通用的定位方式是find_element
,它也能夠實現對全部控件進行查找,可是相對於find
,能夠對屬性類型進行指定。
1
2
3
4
|
[7] pry(main)> find_element(:class_name, 'UIATextField')
#<Selenium::WebDriver::Element:0x31d87e3848df8804 id="3">
[8] pry(main)> find_element(:class_name, 'UIATextField').value
"Email Address"
|
不過在實踐中發現,採用find
、find_element
這類通用的定位方式並很差用,由於定位結果常常不是咱們指望的。
通過反覆摸索,我推薦根據目標控件的類型,選擇對應的定位方式。總結起來,主要有如下三種方式。
針對Button類型的控件(UIAButton),採用button_exact
進行定位:
1
2
|
[9] pry(main)> button_exact('Login')
#<Selenium::WebDriver::Element:0x..feaebd8302b6d77cc id="4">
|
針對Text類型的控件(UIAStaticText),採用text_exact
進行定位:
1
2
|
[10] pry(main)> text_exact('Phantom')
#<Selenium::WebDriver::Element:0x1347e89100fdcee2 id="5">
|
針對控件類型進行定位時,採用tag
;以下方式等價於find_element(:class_name, 'UIASecureTextField')
。
1
2
|
[11] pry(main)> tag('UIASecureTextField')
#<Selenium::WebDriver::Element:0x..fc6f5efd05a82cdca id="6">
|
基本上,這三種方式就已經足夠應付絕大多數測試場景了。固然,這三種方式只是我我的通過實踐後選擇的定位方式,除了這三種,Appium
還支持不少種其它定位方式,你們可自行查看Appium
官方文檔進行選擇。
另外,除了對控件進行定位,有時候咱們還想判斷當前屏幕中是否存在某個控件(一般用於結果檢測判斷),這要怎麼作呢?
一種方式是藉助於Appium
的控件查找機制,即找不到控件時會拋出異常(Selenium::WebDriver::Error::NoSuchElementError
);反過來,當查找某個控件拋出異常時,則說明當前屏幕中不存在該控件。
1
2
3
|
[12] pry(main)> button_exact('Login_invalid')
Selenium::WebDriver::Error::NoSuchElementError: An element could not be located on the page using the given search parameters.
from /Library/Ruby/Gems/2.0.0/gems/appium_lib-8.0.2/lib/appium_lib/common/helper.rb:218:in `_no_such_element'
|
該種方式可行,但比較暴力,基本上不會採用這種方式。
另外一種更好的方式是,查找當前屏幕中指定控件的個數,若個數不爲零,則說明控件存在。具體操做上,將button_exact
替換爲buttons_exact
,將text_exact
替換爲texts_exact
。
1
2
3
4
|
[12] pry(main)> buttons_exact('Login').count
1
[13] pry(main)> buttons_exact('Login_invalid').count
0
|
除此以外,基於Ruby實現的appium_lib
還支持exists
方法,可直接返回Boolean值。
1
2
3
4
|
[14] pry(main)> exists { button_exact('Login') }
true
[15] pry(main)> exists { button_exact('Login_invalid') }
false
|
定位到具體的控件後,操做就比較容易了。
操做類型很少,最經常使用就是點擊(click)和輸入(type),這兩個操做能覆蓋80%以上的場景。
對於點擊操做,才定位到的控件後面添加.click
方法;對於輸入操做,在定位到的輸入框控件後面添加.type
方法,並傳入輸入值。
例如,帳號登陸操做就包含輸入和點擊兩種操做類型。
1
2
3
4
5
6
|
[16] pry(main)> find_element(:class_name, 'UIATextField').type 'leo.lee@debugtalk.com'
""
[17] pry(main)> find_element(:class_name, 'UIASecureTextField').type '123456'
""
[18] pry(main)> button_exact('Login').click
nil
|
在本文中,咱們學習了對iOS UI控件進行交互操做的通常性方法,爲編寫自動化測試腳本打好了基礎。
在下一篇文章中,咱們就要正式開始針對iOS應用編寫自動化測試腳本了。