從0到1搭建移動App功能自動化測試平臺(2):操做iOS應用的控件

轉載: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

關於UI控件

在上一篇文章中,咱們成功地經過Appium Inspector調用模擬器並運行iOS應用,iOS的自動化測試環境也已所有準備就緒了。工具

那麼接下來,咱們就能夠開始實現自動化測試了麼?post

貌似還不行。在開始以前,咱們先想下什麼是APP功能自動化測試。學習

APP的功能自動化測試,簡單地來講,就是讓功能測試用例自動地在APP上執行。具體到每個測試用例,就是能模擬用戶行爲對UI控件進行操做,自動化地實現一個功能點或者一個流程的操做。再細分到每一步,就是對UI控件進行操做。測試

所以,在正式開始編寫自動化測試用例以前,咱們還須要熟悉如何與APP的UI控件進行交互操做。

在iOS系統中,UI控件有多種類型,常見的有按鈕(UIAButton)、文本(UIAStaticText)、輸入框(UIATextField)等等。但無論是對什麼類型的UI控件進行操做,基本均可以分解爲三步,首先是獲取目標控件的屬性信息,而後是對目標控件進行定位,最後是對定位到的控件執行動做。

獲取UI控件信息

在Appium中,要獲取iOS的UI控件元素信息,能夠採用兩種方式:一種是在前一篇文章中提到的Appium Inspector,另外一種是藉助Ruby實現的appium_console,在Terminal中經過命令進行查詢。

Appium Inspector

運行Appium Server,並啓動【Inspector】後,總體界面以下圖所示。

Appium inspector introduction

現對照着這張圖對Appium Inspector進行介紹。

在右邊部分,是啓動的模擬器,裏面運行着咱們的待測APP。咱們能夠像在真機中同樣,在模擬器中執行任意功能的操做,固然,模擬器跟真機畢竟仍是有區別的,跟傳感器相關的功能,例如攝像頭、重力感應等,是無法實現的。

在左邊部分,就是Appium Inspector。Inspector主要由以下四個部分組成:

  • 預覽界面區:顯示畫面與模擬器界面一致;不過,當咱們在模擬器中切換界面後,Inspector的預覽區中顯示圖像並不會自動同步,若要同步,須要點擊【Refresh】按鈕,而後Inspector會將模擬器當前UI信息dump後顯示到預覽區;在預覽區中,能夠點擊選擇任意UI控件。
  • UI信息展現區:展現當前界面預覽區中全部UI元素的層級關係和UI元素的詳細信息;在預覽區中點擊選擇任意UI控件後,在「Details」信息框中展現選中控件的詳細信息,包括name、label、value、xpath等屬性值;經過層級關係,咱們也能瞭解選中控件在當前界面樹狀結構中所處的具體位置。
  • 交互操做區:模擬用戶在設備上的操做,例如單擊(tap)、滑動(swipe)、晃動(shake)、輸入(input)等;操做動做是針對預覽界面區選中的控件,所以在操做以前,務必須要先在預覽區點擊選擇UI元素。
  • 腳本生成區:將用戶行爲轉換爲腳本代碼;點擊【Record】按鈕後,會彈出代碼區域;在交互操做區進行操做後,就會實時生成對應的腳本代碼;代碼語言可經過下拉框進行選擇,當前支持的語言類型有:C#、Ruby、Objective-C、Java、node.js、Python。

在實踐操做中,Inspector最大的用途就是在能夠可視化地查看UI元素信息,而且能夠將操做轉換爲腳本代碼,這對初學者尤其有用。

例如,在預覽區點擊選中按鈕「BUY NOW」,而後在UI信息展現區的Details窗口就能夠看到該按鈕的全部屬性信息。在交互操做區點擊【Tap】按鈕後,就會模擬用戶點擊「BUY NOW」按鈕,而且在腳本區域生成當次按鈕點擊的腳本(選擇Ruby語言):

1
find_element( :name, "BUY NOW >").click

如上就是使用Appium Inspector的通常性流程。

Appium Ruby Console

有了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 Consolearc,便是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返回的控件信息已經足夠知足絕大多數場景需求,但有時候狀況比較特殊,須要enabledxpathvisible、座標等屬性信息,這時就能夠經過執行source命令。執行source命令後,就能夠返回當前屏幕中全部控件的全部信息,以xml格式進行展示。

定位UI控件

獲取到UI控件的屬性信息後,就能夠對控件進行定位了。

首先介紹下最通用的定位方式,find。經過find命令,能夠實如今控件的諸多屬性值(namelabelvaluehint)中查找目標值。查詢時不區分大小寫,若是匹配結果有多個,則只返回第一個結果。

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"

不過在實踐中發現,採用findfind_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

To be continued …

在本文中,咱們學習了對iOS UI控件進行交互操做的通常性方法,爲編寫自動化測試腳本打好了基礎。

在下一篇文章中,咱們就要正式開始針對iOS應用編寫自動化測試腳本了。

相關文章
相關標籤/搜索