IOS9每天多一點(diǎn)了解2:UI測(cè)試
自動(dòng)化測(cè)試用戶界面工具對(duì)于開發(fā)軟件來說是非常有用的,他可以快速的幫你定位到問題。一套成功的測(cè)試流程,可以為你最終發(fā)布軟件帶來信心。在iOS平臺(tái)上,我們使用 Automation 來完成這個(gè)工作。這要打開一個(gè)單獨(dú)的應(yīng)用 Instruments,然后編寫和運(yùn)行 JavaScript 腳本。整個(gè)流程痛苦且漫長(zhǎng)。
UI Testing
在Xcode7中,蘋果介紹了一種新的方式來管理你的應(yīng)用界面的測(cè)試工作。UI testing 允許你對(duì) UI 元素進(jìn)行查找,交互,驗(yàn)證屬性和狀態(tài)。在 Xcode7 中,UI testing 伴隨著測(cè)試報(bào)告,并且和單元測(cè)試一起運(yùn)行。 XCTest 是在 Xcode 5 時(shí)融入到測(cè)試框架的,在Xcode7 中,新增了對(duì) UI 的測(cè)試能力。允許在特定點(diǎn)設(shè)置斷言,查看UI當(dāng)時(shí)的狀態(tài)。
Accessibility(輔助功能)
為了 UI Testing 能夠工作,框架需要和你的眾多元素直接建立連接,然后安排好操作。你可以設(shè)定義特別的點(diǎn),或者在某個(gè) UI 上創(chuàng)建 tweak,然后指定點(diǎn)擊或者滑動(dòng)操作。但是這在不同尺寸設(shè)備上就失效了。
這時(shí)候 accessibility,就能提供幫助了。Accessibility 是蘋果早就發(fā)布的一個(gè)框架,提供給有一定身體障礙(例如失明)的人使用,讓他們能夠操作和使用你的應(yīng)用。他把你的 UI 以語義話的方式提供給這些用戶,允許他們進(jìn)行豐富的操作。你可以(也應(yīng)該)讓你的元素具備Accessibility的能力。有很多原因,比如說自定義的控件,不能夠被自動(dòng)發(fā)現(xiàn)。
UI Testing 有能力通過你的應(yīng)用提供給 Accessibility 的特性,來對(duì)不同尺寸的設(shè)備進(jìn)行測(cè)試提供解決方案。也保證了你在重新組織了一下你的 UI 之后,不必全部重新寫一套測(cè)試。不僅能夠幫助你測(cè)試自己的 UI,同時(shí)也能夠?qū)δ愕膽?yīng)用,更好的支持有一定身體障礙的人群使用而帶來幫助。
UI 錄制
一旦你設(shè)置好了你的 accessible UI,你將要?jiǎng)?chuàng)建 UI 的測(cè)試項(xiàng)。編寫 UI 的測(cè)試是非常耗時(shí),無聊的,如果你的 UI 比較復(fù)雜,也是非常困難的。感謝 Xcode7, 蘋果介紹了 UI Recording. 他允許你新建、或者在已有項(xiàng)目中創(chuàng)建測(cè)試。當(dāng)你打開時(shí),測(cè)試代碼會(huì)隨著你在設(shè)備或模擬器上操作自動(dòng)創(chuàng)建。好了,簡(jiǎn)介到此結(jié)束,是時(shí)候用一個(gè)例子來看一下,如何使用了。
創(chuàng)建 UI 測(cè)試?yán)?/strong>
我們將通過UI Testing套件來創(chuàng)建一個(gè)實(shí)例,展示 UI Testing 是如何工作的。最終的demo 可以在Github下載,你可以跟著我們一起來練習(xí)和查看結(jié)果。
創(chuàng)建
在Xcode7中,當(dāng)你創(chuàng)建新項(xiàng)目時(shí),如果選擇包含 UI Tests,將會(huì)為你創(chuàng)建一個(gè)新的 target,你可以在彈出框中設(shè)置所有你想要的配置。
本項(xiàng)目非常簡(jiǎn)單,但已經(jīng)足夠幫我們演示 UI Testing 在 Xcode 7 中是如何工作的了。
這里有一個(gè) menuViewController,里面包含一個(gè) switch 和一個(gè) button。點(diǎn)擊 button,可以 push 到 detailViewController 頁面。當(dāng) switch 的狀態(tài)為 off 的時(shí)候,是禁止 push 的。詳細(xì)頁面有一個(gè)按鈕和一個(gè)標(biāo)簽,點(diǎn)擊按鈕可以增加標(biāo)簽的值。
使用 UI Recording
一旦UI控件創(chuàng)建好,并且寫好了方法。我們就可以寫測(cè)試單元,確保代碼的變化,不會(huì)影響方法的效果。
The XCTest UI Testing API
在我們錄制測(cè)試的動(dòng)作之前,我們需要決定斷言放在哪里。為了能夠測(cè)試我們的UI,我們可以使用 XCTest Framework,他現(xiàn)在擴(kuò)充了三個(gè)新的 API。
- XCUIApplication 這是你要測(cè)試的應(yīng)用的代理,他能夠把你的應(yīng)用啟動(dòng)起來,并且每次都在一個(gè)新進(jìn)程中。這可能會(huì)花一點(diǎn)兒時(shí)間,但這意味著每次要測(cè)試你的應(yīng)用時(shí),他都會(huì)把需要處理的工作完成,保持一個(gè)干凈的,全新的狀態(tài)。
- XCUIElement 這是你要測(cè)試的應(yīng)用的 UI 元素的代理。元素都有類型和唯一標(biāo)識(shí)。你可以結(jié)合使用來找到你的元素在哪里,這些元素以樹狀結(jié)構(gòu)組合,構(gòu)成了你的應(yīng)用的表現(xiàn)形式。
- XCUIElementQuery 當(dāng)你要要查找元素時(shí),會(huì)用到 XCUIElementQuery, 每一個(gè) XCUIElement 基于一個(gè)查詢。這個(gè)查詢必須在你的元素樹上找到對(duì)應(yīng)的元素,否則就會(huì)失敗。異常信息會(huì)提示不存在,你可以去檢查一下是否展現(xiàn)在樹上了。XCUIElementQuery 具有通用性,如果你查找一個(gè)輔助功能支持的元素,查詢會(huì)返回一組結(jié)果。
現(xiàn)在我們準(zhǔn)備好寫測(cè)試了,通過這個(gè)測(cè)試來進(jìn)一步解釋提到的這些API。
Test 1 - 確保switch關(guān)閉時(shí),導(dǎo)航不能生效
首先我們定義一個(gè)測(cè)試方法。
- func testTapViewDetailWhenSwitchIsOffDoesNothing() {
- }
定義好方法后,我們讓光標(biāo)移到方法中,點(diǎn)擊Xcode窗口底部的錄制按鈕。
現(xiàn)在應(yīng)用啟動(dòng)起來了,點(diǎn)擊一下 switch, 讓其處于關(guān)閉狀態(tài),然后點(diǎn)擊一下“View Detail”按鈕,下面這些代碼會(huì)自動(dòng)插入到 `testTapViewDetailWhenSwitchIsOffDoesNothing`方法中.
- let app = XCUIApplication()
- app.switches["View Detail Enabled Switch"].tap()
- app.buttons["View Detail"].tap()
現(xiàn)在再點(diǎn)一下錄制按鈕,錄制會(huì)停止下來。可以看到,實(shí)際上并沒有 push 到 detailViewController 頁面。但這時(shí)測(cè)試并不知道,我們要加個(gè)斷言,判斷一下沒有變化。我們可以比較導(dǎo)航欄的標(biāo)題值,這并不優(yōu)雅,但當(dāng)前演示就夠了。
- XCTAssertEqual(app.navigationBars.element.identifier, "Menu")
添加了這一行斷言后運(yùn)行,發(fā)現(xiàn)測(cè)試還是能夠通過。如果你把導(dǎo)航欄的標(biāo)題改為“Detail”,你會(huì)發(fā)現(xiàn)測(cè)試通過不了了。下面試最終的測(cè)試代碼,加了一些解釋行為的注釋。
#p#
Test 2 - 確保 switch 的狀態(tài)為 on 時(shí),導(dǎo)航可以正常工作
第二個(gè)測(cè)試前面的非常相似,我們就不細(xì)講了。唯一的區(qū)別是switch是可用狀態(tài),所以應(yīng)用會(huì)加載詳細(xì)頁面到屏幕上。XCTAssertEqual 方法來驗(yàn)證是否正確。
Test 3 - 確保增長(zhǎng)按鈕確實(shí)增加了標(biāo)簽的值
在這個(gè)測(cè)試中,我們要驗(yàn)證點(diǎn)擊了增長(zhǎng)按鈕,標(biāo)簽的值是否會(huì)加1.前兩行代碼和前面的例子很像,我們復(fù)制過來。
- let app = XCUIApplication()
- // Tap the view detail button to open the detail page.
- app.buttons["View Detail"].tap()
下一步我們要得到button,我們將多點(diǎn)擊幾下。所以我們要把按鈕作為一個(gè)變量。我們不必手寫代碼和 debug 他。再次錄制并且點(diǎn)擊一下 increase 的按鈕,這會(huì)自動(dòng)給你添加下面的代碼。
- app.buttons["Increment Value"].tap()
我們停止錄制,把代碼改成下面這樣。
- let incrementButton = app.buttons["Increment Value"]
這種方法,讓我們不必手動(dòng)編寫代碼,同樣的方式,我們獲的 lable 變量。
- let valueLabel = app.staticTexts["Number Value Label"]
現(xiàn)在我們得到了能夠交互的感興趣的元素。下面這個(gè)測(cè)試中,我們測(cè)試點(diǎn)擊10次 button 按鈕,看標(biāo)簽的值是否隨之增長(zhǎng)。我們可以錄制10遍,但既然我們得到了變量,我們可以寫一個(gè)循環(huán)來測(cè)試它。
這三個(gè)測(cè)試離一個(gè)完整的測(cè)試距離甚遠(yuǎn),但給你展示了一個(gè)很好的開始,你可以輕松的擴(kuò)展開來。為什么不寫一個(gè)自己的測(cè)試練習(xí)一下呢。比如去驗(yàn)證一下 button 是 enable 的 時(shí)候,你可以在switch 關(guān)閉的情況下導(dǎo)航成功呢?
當(dāng)錄制發(fā)生錯(cuò)誤時(shí)
有時(shí)候你發(fā)現(xiàn)你在錄制的時(shí)候,點(diǎn)擊了一個(gè)元素,但是產(chǎn)生的代碼看起來不正確。通常這是由于你的元素對(duì)于 Accessibility 是不可用的。為了確定是否是這個(gè)原因,你可以打開Xcode的Accessibility Inspector。
一旦打開Accessibility Inspector,如果你按下CMD+F7,鼠標(biāo)懸浮在元素上時(shí),你可以在熱點(diǎn)下面,看到完整的元素的信息。這能夠在你找不到元素時(shí)給你提供一些線索。
一旦你找到問題所在,你可以打開 Interface Builder.在屬性攔里找到 Accessibility 欄。他允許你設(shè)置元素的 accessibility。這是個(gè)強(qiáng)大的工具,設(shè)置你的圖形接口的 accessibility 屬性。
當(dāng)測(cè)試失敗時(shí)
當(dāng)測(cè)試失敗時(shí),如果你不確定為什么?有很多辦法能夠幫你修復(fù)錯(cuò)誤。首先,你可以去測(cè)試報(bào)告里看一看。
當(dāng)你打開這個(gè)視圖,將鼠標(biāo)懸停在某一步上時(shí),你會(huì)發(fā)現(xiàn)在方法的右面有一個(gè)小的眼睛圖標(biāo)。點(diǎn)擊一下這個(gè)眼睛圖標(biāo),會(huì)給你一個(gè)當(dāng)時(shí)的截圖,你能夠清晰的看到當(dāng)時(shí)你的UI的狀態(tài)以便發(fā)現(xiàn)錯(cuò)誤。
和單元測(cè)試一樣,你可以設(shè)置斷點(diǎn),允許你更方便的發(fā)現(xiàn)問題。你可以輸出UI的層次結(jié)構(gòu),元素的屬性等,然后找到原因。
為什么要進(jìn)行UI測(cè)試
UI自動(dòng)化測(cè)試是一個(gè)很好的辦法,為你在修改應(yīng)用時(shí),提高信心,并提供質(zhì)量保證,。我們已經(jīng)看到了,在 Xcode 中添加 UI 測(cè)試和運(yùn)行是多么簡(jiǎn)單。他不僅幫助你發(fā)現(xiàn)問題,并且能夠?qū)τ胁糠止δ苷系K的人使用你的應(yīng)用提供幫助。
Xcode 的一個(gè)特別好的特性是,可以從 continuous integration server 來測(cè)試你的應(yīng)用。這個(gè)可以利用 Xcode 的機(jī)器人來進(jìn)行測(cè)試,并且 from the command line 意味著,如果一個(gè)測(cè)試失敗,你會(huì)第一時(shí)間被通知到。