從 0 到 1 搭建移動 App 功能自動化測試平台 (2):操作 iOS 應用的控制項

寫在前面

前兩天微信突然發來一條系統消息,提示DebugTalk可以開通原創標識了(同時也有了評論功能),雖然一直在期待,但沒想到來得這麼快,著實是個不小的驚喜。

另外,最近在公眾號後台也收到好幾個朋友的信息,有的是詢問某某部分什麼時候能發布,有的是希望能加快更新速度。說實話,收到這樣的信息雖然會有壓力,但真的挺開心的,因為這說明DebugTalk至少能給一部分人帶去價值,這說明這件事本身還是值得堅持去做的。

不過,在更新頻率這件事兒上,的確是要跟大家說抱歉了。因為DebugTalk發布的內容全都是原創,主題基本上都是來源於我日常測試工作的經驗積累,或者我近期學習一些測試技術的收穫總結,這也意味著,我寫的東西很多時候並不是自己完全熟悉的(完全掌握的東西也沒有足夠的動力專門花時間去寫)。

就拿最近連載的《從0到1搭建移動App功能自動化測試平台》系列來說,由於我也是邊探索邊總結,因此中途難免會遇到一些意想不到的坑,造成額外的耗時,而且為了保證文章能盡量通俗易通,我也需要對涉及到的內容充分進行理解,並且經過大量實踐進行驗證,然後才能站在半個初學者、半個過來人的角度,重新整理思路,最後以儘可能流暢的思路將主題內容講解清楚。

基於這些原因,DebugTalk要做到每日更新是很難了,但是保證每周發布1~2篇還是可以的,希望大家能理解。

關於UI控制項

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

那麼接下來,我們就可以開始實現自動化測試了么?

貌似還不行。在開始之前,我們先想下什麼是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進行介紹。

在右邊部分,是啟動的模擬器,裡面運行著我們的待測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語言):

find_element(:name, "BUY NOW >").clickn

如上就是使用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,內容形式如下所示:

[caps]nplatformName = "ios"nplatformVersion = 9.3,napp = "/path/to/UICatalog.app.zip"ndeviceName = "iPhone Simulator"n

其中,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首字母的組合。

? lsnappium.txtn? arcn[1] pry(main)>n

接下來,就可以通過執行命令查詢當前設備屏幕中的控制項信息。

使用頻率最高的一個命令是page,通過這個命令可以查看到當前屏幕中所有控制項的基本信息。

例如,當屏幕停留在前面截圖中的頁面時,執行page命令可以得到如下內容。

[1] pry(main)> pagenUIANavigationBarn name: HomeViewn id: Home => Homen 米 => mn 去看看 => ViewnUIAButtonn name, label: tabbar category graynUIAImagen name: dji_logo.pngnUIAButtonn name, label: tabbar cart graynUIATableViewn value: rows 1 to 4 of 15nUIAPageIndicatorn value: page 2 of 2nUIATableCelln name: For the first time ever in a hand held camera, the Osmo brings professional, realtime cinema-quality stabilization.n id: 米 => mnUIAStaticTextn name, label, value: For the first time ever in a hand held camera, the Osmo brings professional, realtime cinema-quality stabilization.n id: 米 => mnUIAStaticTextn name, label, value: OSMOnUIAButtonn name, label: SHOP NOW >nUIATableCelln name: RoninnUIAStaticTextn name, label, value: RoninnUIAStaticTextn name, label, value: Phantomn id: 米 => mn... (略)nUIAButtonn name, label: Storen value: 1n id: 門店 => Storen... (略)nUIAButtonn name, label: My Accountn id: My Account => My Accountnniln

通過返回信息,我們就可以看到所有控制項的type、name、label、value屬性值。如果在某個控制項下沒有顯示label或value,這是因為這個值為空,我們可以不予理會。

由於page返回的信息太多,可能不便於查看,因此在使用page命令時,也可以指定控制項的類型,相當於對當前屏幕的控制項進行篩選,只返回指定類型的控制項信息。

指定控制項類型時,可以通過string類型進行指定(如 page "Image"),也可通過symbol類型進行指定(如 page :cell)。指定的類型可只填寫部分內容,並且不分區大小寫。

[2] pry(main)> page "Image"nUIAImagen name: dji_logo.pngnniln[3] pry(main)> page :cellnUIATableCelln name: DJI』s smartest flying camera ever.n id: 米 => mnUIATableCelln name: RoninnUIATableCelln name: Phantomn id: 米 => mnniln

如果需要查看當前屏幕的所有控制項類型,可以執行page_class命令進行查看。

[4] pry(main)> page_classn14x UIAButtonn8x UIAStaticTextn4x UIAElementn4x UIATableCelln2x UIAImagen2x UIAWindown1x UIAPageIndicatorn1x UIATableViewn1x UIAStatusBarn1x UIANavigationBarn1x UIATabBarn1x UIAApplicationnniln

基本上,page返回的控制項信息已經足夠滿足絕大多數場景需求,但有時候情況比較特殊,需要enabled、xpath、visible、坐標等屬性信息,這時就可以通過執行source命令。執行source命令後,就可以返回當前屏幕中所有控制項的所有信息,以xml格式進行展現。

定位UI控制項

獲取到UI控制項的屬性信息後,就可以對控制項進行定位了。

首先介紹下最通用的定位方式,find。通過find命令,可以實現在控制項的諸多屬性值(name、label、value、hint)中查找目標值。查詢時不區分大小寫,如果匹配結果有多個,則只返回第一個結果。

[5] pry(main)> find(osmo)n#<Selenium::WebDriver::Element:0x..febd52a30dcdfea32 id="2">n[6] pry(main)> find(osmo).labeln"Osmo"n

另一個通用的定位方式是find_element,它也可以實現對所有控制項進行查找,但是相對於find,可以對屬性類型進行指定。

[7] pry(main)> find_element(:class_name, UIATextField)n#<Selenium::WebDriver::Element:0x31d87e3848df8804 id="3">n[8] pry(main)> find_element(:class_name, UIATextField).valuen"Email Address"n

不過在實踐中發現,採用find、find_element這類通用的定位方式並不好用,因為定位結果經常不是我們期望的。

經過反覆摸索,我推薦根據目標控制項的類型,選擇對應的定位方式。總結起來,主要有以下三種方式。

針對Button類型的控制項(UIAButton),採用button_exact進行定位:

[9] pry(main)> button_exact(Login)n#<Selenium::WebDriver::Element:0x..feaebd8302b6d77cc id="4">n

針對Text類型的控制項(UIAStaticText),採用text_exact進行定位:

[10] pry(main)> text_exact(Phantom)n#<Selenium::WebDriver::Element:0x1347e89100fdcee2 id="5">n

針對控制項類型進行定位時,採用tag;如下方式等價於find_element(:class_name, UIASecureTextField)。

[11] pry(main)> tag(UIASecureTextField)n#<Selenium::WebDriver::Element:0x..fc6f5efd05a82cdca id="6">n

基本上,這三種方式就已經足夠應付絕大多數測試場景了。當然,這三種方式只是我個人經過實踐後選擇的定位方式,除了這三種,Appium還支持很多種其它定位方式,大家可自行查看Appium官方文檔進行選擇。

另外,除了對控制項進行定位,有時候我們還想判斷當前屏幕中是否存在某個控制項(通常用於結果檢測判斷),這要怎麼做呢?

一種方式是藉助於Appium的控制項查找機制,即找不到控制項時會拋出異常(Selenium::WebDriver::Error::NoSuchElementError);反過來,當查找某個控制項拋出異常時,則說明當前屏幕中不存在該控制項。

[12] pry(main)> button_exact(Login_invalid)nSelenium::WebDriver::Error::NoSuchElementError: An element could not be located on the page using the given search parameters.nfrom /Library/Ruby/Gems/2.0.0/gems/appium_lib-8.0.2/lib/appium_lib/common/helper.rb:218:in `_no_such_elementn

該種方式可行,但比較暴力,基本上不會採用這種方式。

另一種更好的方式是,查找當前屏幕中指定控制項的個數,若個數不為零,則說明控制項存在。具體操作上,將button_exact替換為buttons_exact,將text_exact替換為texts_exact。

[12] pry(main)> buttons_exact(Login).countn1n[13] pry(main)> buttons_exact(Login_invalid).countn0n

除此之外,基於Ruby實現的appium_lib還支持exists方法,可直接返回Boolean值。

[14] pry(main)> exists { button_exact(Login) }ntruen[15] pry(main)> exists { button_exact(Login_invalid) }nfalsen

對控制項執行操作

定位到具體的控制項後,操作就比較容易了。

操作類型不多,最常用就是點擊(click)和輸入(type),這兩個操作能覆蓋80%以上的場景。

對於點擊操作,才定位到的控制項後面添加.click方法;對於輸入操作,在定位到的輸入框控制項後面添加.type方法,並傳入輸入值。

例如,賬號登錄操作就包含輸入和點擊兩種操作類型。

[16] pry(main)> find_element(:class_name, UIATextField).type leo.lee@dji.comn""n[17] pry(main)> find_element(:class_name, UIASecureTextField).type 123456n""n[18] pry(main)> button_exact(Login).clicknniln

To be continued ...

在本文中,我們學習了對iOS UI控制項進行交互操作的一般性方法,為編寫自動化測試腳本打好了基礎。

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

Read More ...

公眾號:DebugTalk

原文鏈接:debugtalk.com/post/buil

相關文章

  • 《從0到1搭建移動App功能自動化測試平台(0)背景介紹和平台規劃》
  • 《從0到1搭建移動App功能自動化測試平台(1):模擬器中運行iOS應用》

推薦閱讀:

TAG:测试开发 | 自动化测试 | iOS |