蘋果公司終於決定在今年的 WWDC 上對用戶界面進行加倍測試,讓我們深入到 API 看看我們能發現什麼.
我從事於 IOS 測試已經有幾個年頭了,在進入 BeerMenus 之前,我在 Pivotal 呆了兩年,Pivots, 我們更願意這樣被稱呼, 嚴謹測試.。作為測試驅動開發公司 (或者 TDD 公司), Pivots花時間來測試每一個角落裂縫。盡管代碼的覆蓋率並不是最優先級的,它們很容易包含95%,並不全是這樣的項目。
Cedar
回到 Xcode 4,測試 iOS 應用的唯一方式就是 OCUnit,其遺留了許多有待改進之處。Pivots 利用了它們在 行為驅動開發(behavior-driven development)框架中的專業觀點著手創建它們自己的測試套件 - Cedar 誕生了.
Cedar 是一個優秀的框架,用於創建 BDD 風格的測試,完全支持匹配器和偽裝。而 Xcode 7 還沒有完全的支持,Pivotal 在一個分支上跟進他們的工作,預計很快就會有東西要發布了。自從嘗到 TDD 的甜頭之後, 我使用 Cedar 測試過我所工作過的每一個 iOS 應用。我甚至開始把它用在功能測試控制器上,但那是下次的主題了。
單獨使用 Cedar 不能給與我在使用 Ruby on Rails 時所日漸贊賞的端到端覆蓋。我不能可靠地在幾個屏幕之間進行觸摸操作,讓應用運作起來。可以理解的是,Cedar 並不用為那個而被創建出來的。為了填補這個空缺,許多玩家都已經創建了他們自己的第三方功能測試框架。
第三方測試
Frank, KIF, Subliminal, Apple 的 UIAutomation,我把他們都試了一遍。你要是希望了解更多可以訪問我的故障特征測試框架。它不是開發者的失敗,而是因為 Apple 對待測試只有有限的開放性。這使得這些框架有一系列的補丁,而在這些補丁之上,這些框架不外乎都成為了一堆破碎的工具。
沒有涉及到的更多細節:
Frank 一直被遺棄。
KIF 已經與主要的 iOS 修訂版本決裂。
Subliminal 不能在命令行中可靠地運行。
UIAutomation 是用 JavaScript 和 clunky 寫的。
我在 Pivotal 工作的時候,有六個分離的 app,我嘗試對每個不同應用使用不同的框架。我甚至對 KIF 和 Frank 捐助,因為我希望這些框架成功。不幸地是,它們不可能離開 Apple 的支持。
WWDC 2015
今年的 WWDC 帶來了好消息:在各平台進展狀態介紹環節對用戶界面測試進行了介紹。
Xcode 7 引入了用戶界面測試,旨在確保您在代碼中所做的修改,不會向您的用戶呈現出乎意料的效果。
Wil Turner 和 Brooke Callahan 在第 406 節展示了新框架,Xcode 中的 UI 測試。如果你還沒有看過它,建議去看一看。他們演示了一個簡單的任務管理應用,對工具中的一些 API 和功能進行了高亮顯示。
UI 測試的重頭戲是“記錄”。在你有了一個要位置工作的應用程序之後,點擊一個大紅圈,然後開始在你的 app 裡面到處點擊。當你正在與 app 交互時,代碼會被自動生成出來以重現你的操作流程。理論上,之後你就能夠運行新創建的測試,並看著 app 重復你之前的動作。
UI 測試實戰
自賣自誇是一回事,在真實世界中這個框架會起作用嗎?
文檔
關於這個新框架你可能知道的首要事情之一就是文檔的缺乏. iOS 9 和 Xcode 7 都沒有包含 UI 測試的任何官方的文檔。幸運的是,新的 XCU* 類中的大多數都擁有良好的注釋的頭文件。借助於 appledoc,我可以把這些頭文件拖入 Xcode 兼容 Dash 的文件集合中。只要把資源庫 clone 下來,將文件打開;默認的文檔浏覽器就會添加這些新的 API。你還可以在線查看文檔。
UI 測試位於 XCTest 框架,添加了4個新的類,1個新的協議,以及3個新的常量。
這是用來啟動你要測試的應用程序的主要拉鉤。你可創建一個新的實例,[[XCUIApplication alloc] init], 然後在它上面調用 -launch。 如果你向應用添加了一個新的 UI 測試目標,就會在模板裡面看見一個示例。在啟動之前,你可以通過設置 -launchArguments 或者 -launchEnvironment 屬性來分別設定一個特殊的參數或者環境變量。這樣會創建一個漂亮的布局,例如,向你的 HTTP 客戶端告知,在測試時訪問一個模擬的服務器.
XCUIApplication *app = [[XCUIApplication alloc] init];
app.launchArguments = @[ @"USE_MOCK_SERVER" ];
[app launch];
你還可以 -terminate 你的 app,但我還沒有發現過對那個東西的使用。
你可以使用 +sharedDevice 創建這個對象的一個新實例。唯一可以被公共的訪問到的屬性是-orientation,它會返回普通的 UIDeviceOrientation 枚舉值。雖然這是一個可讀可寫的屬性,但是對他進行了設置並不會對模擬器的界面進行更新。我不確定這是不是我正在使用的 beta 版的一個 bug。
這將是 UI 測試中使用率最高的一類,因它可用來構建查詢以定位元素。還記得 UI 自動化裡的 app.tableViews[0].cells[0] 嗎?二者極其相似。
[[app.tables.element.cells elementAtIndex:4] tap];
每次調用堆棧都會返回一個可被鏈接在一起的查詢對象,這就讓控制應用程序變得更加精確。提到-element(元素),你便會說“我知道這兒只有一個,我要的就是這個”。你還可以通過特定的標簽來訪問堆棧中的對象。
[app.tables.element.cells[@"Call Mom"].buttons[@"More"] tap];
這種便捷的方式將通過 -elementMatchingType:identifier: 和 XCUIElementTypeAny,以及你傳遞的字符串來實現。如果想要更精確地控制所有選擇,通過元素查詢 XCUIElementType 將是最佳的方式。
元素是封裝了需要在一個應用程序中動態放置一個用戶界面的相關信息的對象。元素用查詢的方式被描述。 當調用到一個事件API, 元素就會被解析到。如果發現有0個或者多個匹配,就會冒出一個錯誤。
創建一個 XCUIElementQuery
的方式,會給予你元素的引用。除非你真的到了要與其交互的時候,框架是不會實際去應用的層級中找尋它們的。 這樣為你帶來了保持干淨的測試的好處——你可以保留一個查詢的引用,在測試的不同地方重用它。元素只會在交互期間才會被查找,這樣就為你節省了再一次深入獲取的花費。
交互都被進行了恰當的命名,會像你所期望的那樣執行動作。例如,在 iOS 中你可以使用 -tap
, -pressForDuration:
, -swipeUp
, 以及 -twoFingerTap
。 如果你測試的是一個 OS X 應用,你可以對應地使用-click*
交互. 你甚至還可以使用 +performWithKeyModifiers:block:
來模擬持續的按下 ⌘
按鍵.
一個測試框架裡怎麼可能沒有斷言?由於 UI 測試只是 XCTest 的擴展,所有已有的斷言仍然管用,你仍然可以使用像 XCTAssert(),XCTAssertEquals(),和 XCTAssertNotNil() 這樣的斷言宏。
檢查某個元素是否存在
一個快速的方法就是在一個元素對象上進行非空斷言測試:
XCTAssertNotNil(app.buttons[@"Submit"]);
但是,elementMatchingType:identifier:總是返回一個對象,在這這個函數解析完之前,由它來決定了一個元素是否真的與查詢相匹配,這說明這個測試總是通過的。作為替代,在斷言中通過在元素上調用 -exist 方法來包裝一下,像這樣:
XCTAssert(app.buttons[@"Submit"].exists);
另一個建議就是自始至終都不用斷言,那���在運行測試的時候如果解析出的查詢結果並不能匹配到一個元素則測試就會自動失敗。如果想這樣做,別忘了在 setup 方法裡關閉(注釋)[XCTestCase -continueAfterFailure] 這項。
我經常用到的調試方法是打斷點或者在控制台輸出對象的內容(我知道這難以置信)。在測試代碼中設置斷點並且命中斷點,調試器就會定位到問題所在。但是你不能在一個 app 的代碼裡加裝調試器。由於打印的日志語句會在控制台裡顯示,所以要利用這個來調試。
當在控制台輸出 XCUIElementQuery 的結果時,馬上就會出現許多無關的干擾輸出。試想一個表裡有標著"1"到"5"標簽的五個單元。觸摸帶有標簽"3"的單元時候你可以打印如下的日志(為了清晰顯示,這裡忽略一些關鍵字輸出):
(lldb) po app.tables.element.cells[@"Three"]
Query chain:
→Find: Target Application
↪︎Find: Descendants matching type Table
Input: {
Application:{ {0.0, 0.0}, {375.0, 667.0} }, label: "Demo"
}
Output: {
Table: { {0.0, 0.0}, {375.0, 667.0} }
}
↪︎Find: Descendants matching type Cell
Input: {
Table: { {0.0, 0.0}, {375.0, 667.0} }
}
Output: {
Cell: { {0.0, 64.0}, {375.0, 44.0} }, label: "One"
Cell: { {0.0, 108.0}, {375.0, 44.0} }, label: "Two"
Cell: { {0.0, 152.0}, {375.0, 44.0} }, label: "Three"
Cell: { {0.0, 196.0}, {375.0, 44.0} }, label: "Four"
Cell: { {0.0, 240.0}, {375.0, 44.0} }, label: "Five"
}
↪︎Find: Elements matching predicate ""Three" IN identifiers"
Input: {
Cell: { {0.0, 64.0}, {375.0, 44.0} }, label: "One"
Cell: { {0.0, 108.0}, {375.0, 44.0} }, label: "Two"
Cell: { {0.0, 152.0}, {375.0, 44.0} }, label: "Three"
Cell: { {0.0, 196.0}, {375.0, 44.0} }, label: "Four"
Cell: { {0.0, 240.0}, {375.0, 44.0} }, label: "Five"
}
Output: {
Cell: { {0.0, 152.0}, {375.0, 44.0} }, label: "Three"
}
它准備好了嗎?
作為 Xcode 7 Beta 2,UI 測試做出了承諾。Xcode 7 Beta 2 是可靠的,它的查詢引擎功能十分強大,並且在未來比任何第三方庫都有用。
然而,它還有些奇怪的地方。目前,它還沒辦法等待元素的出現或者消失。這就以為著,在你繼續工作之前,你無法等待網絡活動的完成。我用盡了所有異步測試方法去循環好用但是老舊的 sleep()。
不幸的是,只要你搞砸了任何 App 的時序,Xcode 就開始卡殼,並且測試就會崩潰。
Xcode 試圖去記錄那些沒有注冊的特定事件。例如,任何與 WKWebView 的交互都必須手寫。幸運的是當請求位置授權時,會與系統警報進行交互。我希望這些問題會在下個 beta 版本發行時能被 GM 修復。
總而言之,我認為每個人都至少應該在自己的項目上使用 UI 測試。它提供了一個驗證你的應用程序的新的方法,而且它還提供了第二層測試說明,App 商店中的任何 App 都能從中受益。讓我興奮的是,未來它將整合進我每天工作的項目中,並且十分期待社區會如何使用它。
VirtualBOX 虛擬機安裝 OS X 10.9 Mavericks 及 Xcode 5 http://www.linuxidc.com/Linux/2014-09/106734.htm
Mac OS xCode5.1 配置 Cocos2d-x 3.0 開發環境 http://www.linuxidc.com/Linux/2014-05/101906.htm
Xcode 的詳細介紹:請點這裡
Xcode 的下載地址:請點這裡
英文原文:UI Testing in Xcode 7