摘要:返回到并把應(yīng)用停下來(lái)測(cè)試前操作在項(xiàng)目瀏覽器中,展開(kāi)項(xiàng)目。如果在這段時(shí)間內(nèi)預(yù)期的東西沒(méi)有顯示,測(cè)試就宣告失敗。點(diǎn)擊或者運(yùn)行測(cè)試。但是作為其他測(cè)試的一部分更有效。測(cè)試運(yùn)行器是不會(huì)自動(dòng)運(yùn)行它的。
用戶(hù)期待iOS應(yīng)用的高水準(zhǔn)用戶(hù)體驗(yàn),因而你需要設(shè)計(jì)、開(kāi)發(fā)和測(cè)試你的應(yīng)用來(lái)滿(mǎn)足這一不斷上升的期望。為了達(dá)成這個(gè)目標(biāo),你會(huì)投入多少時(shí)間進(jìn)行原始人工的用戶(hù)界面測(cè)試?你知道這活兒怎么干…從Xcode啟動(dòng)你的應(yīng)用,并不斷的用手指點(diǎn)擊同一些按鈕來(lái)確保你的設(shè)計(jì)中沒(méi)有退步。相比這些,你當(dāng)然更愿意做其它一些事情?
考慮下使用Xcode 5中增強(qiáng)的UI測(cè)試,還有?OS X Server中支持的持續(xù)集成?這篇文章展示了蘋(píng)果公司為開(kāi)發(fā)者貢獻(xiàn)的最好的工具。
你也許會(huì)說(shuō),這很棒,但是如何讓那些簡(jiǎn)單的用戶(hù)動(dòng)作測(cè)試變得自動(dòng)化呢,例如確保一個(gè)在合適區(qū)域的雙擊或觸摸后會(huì)進(jìn)入正確的視圖?即使是測(cè)試腳本和機(jī)器人也不會(huì)有能在屏幕上滑動(dòng)的電容式觸摸手指…呃…它們有手指么?
在這篇教程中,你將會(huì)學(xué)習(xí)到一些有關(guān)?KIF?的東西(Keep it Functional), 它是一個(gè)開(kāi)源的用戶(hù)界面測(cè)試框架. 使用 KIF, 并利用?iOS中的輔助功能?API, 你將能夠編寫(xiě)模擬用戶(hù)輸入(例如點(diǎn)擊、觸摸和文本輸入)的測(cè)試。
它提供自動(dòng)化的、真實(shí)的用戶(hù)界面操作, 幫助放松你的心情,因而你就可以只去關(guān)注自己的殺手級(jí)應(yīng)用了,而不是在UI測(cè)試上耗費(fèi)你職業(yè)生涯的一半時(shí)間。
讓我們開(kāi)始測(cè)試工作吧!
入門(mén)我們以一個(gè)叫做茄薯Solanum(一個(gè)馬鈴薯品種的名字)的計(jì)時(shí)器應(yīng)用為例。這是一個(gè)基于番茄工作法的應(yīng)用。
它是這么運(yùn)作的:
按照設(shè)定好的時(shí)間工作,接著休息一下,然后重復(fù)工作和休息.
經(jīng)過(guò)這樣若干個(gè)循環(huán)之后, 你就會(huì)休息一段更長(zhǎng)的時(shí)間.
這個(gè)應(yīng)用就是一個(gè)能夠持續(xù)跟蹤時(shí)間區(qū)段的簡(jiǎn)單計(jì)時(shí)器。
這款應(yīng)用之后能使你的開(kāi)發(fā)工作變得更加高效!
從?這里?下載并解壓縮入門(mén)項(xiàng)目.注意?KIF 是一個(gè)獨(dú)立的項(xiàng)目,它是為茄薯應(yīng)用構(gòu)建一個(gè)用于測(cè)試目標(biāo)的庫(kù)。因此你需要雙擊?solanum.xcworkspace?以便在Xcode中打開(kāi)項(xiàng)目,而不是?solanum.xcodeproj。在項(xiàng)目導(dǎo)航視窗中找到這兩個(gè)項(xiàng)目,如下圖所示:
將應(yīng)用的目標(biāo)設(shè)置到solanum,然后選擇3.5或者4英寸的iPhone模擬器目標(biāo)。不要使用64位,因?yàn)镵IF在64位模擬器上有一些問(wèn)題。構(gòu)建并運(yùn)行這個(gè)應(yīng)用。四處瞧瞧,了解一下,然后切換到Settings。
應(yīng)用有一個(gè)能夠加速時(shí)間流逝的調(diào)試模式。因此你可以設(shè)置一個(gè)20分鐘的計(jì)時(shí)器,測(cè)試時(shí)花10秒完成這個(gè)過(guò)程。這只是為了幫助你測(cè)試應(yīng)用,你應(yīng)該不會(huì)想花20分鐘等著看它運(yùn)行吧。
開(kāi)啟調(diào)試模式來(lái)加速計(jì)時(shí)器,接著點(diǎn)擊Clear History按鈕,然后在確認(rèn)彈出視圖上點(diǎn)擊Clear。這幾步確保了你是在一個(gè)干凈的環(huán)境中開(kāi)始測(cè)試作業(yè)。返回到Xcode并把應(yīng)用停下來(lái).
測(cè)試前操作在項(xiàng)目瀏覽器中,展開(kāi)solanum項(xiàng)目。 右擊UI測(cè)試文件夾并點(diǎn)擊New File…來(lái)加入你的新測(cè)試用例。
選擇?iOSCocoa TouchObjective-C class?并點(diǎn)擊?Next, 將類(lèi)命名為?UITests?并使其成為?KIFTestCase 的子類(lèi)。
點(diǎn)擊Next并確保文件已經(jīng)被添加到UI Tests目標(biāo)中, 而不是 solanum 目標(biāo)。 然后點(diǎn)擊接下來(lái)Create保存文件.
KIFTestCase是SenTestCase的一個(gè)子類(lèi). 那意味著你擁有了大部分的標(biāo)準(zhǔn) OCUnit 測(cè)試方法和機(jī)制可以使用,如果你已經(jīng)很熟悉單元測(cè)試的話(huà)。
打開(kāi)UITests.m,并在@implementation?一行添加如下的方法:
- (void)beforeAll { [tester tapViewWithAccessibilityLabel:@"Settings"]; [tester setOn:YES forSwitchWithAccessibilityLabel:@"Debug Mode"]; [tester tapViewWithAccessibilityLabel:@"Clear History"]; [tester tapViewWithAccessibilityLabel:@"Clear"]; }
beforeAll?是一個(gè)在所有測(cè)試運(yùn)行之前被調(diào)用一次的特殊方法。你可以為你這里運(yùn)行的測(cè)試設(shè)置任何實(shí)體變量和初始化條件.
tester?對(duì)象是指定的KIFUITestActor?類(lèi)的一個(gè)縮略名稱(chēng)。這個(gè)類(lèi)包含了模擬用戶(hù)動(dòng)作的方法,包括觸摸和滑動(dòng).
tapViewWithAccessibilityLabel 這也許是最常被用到的測(cè)試動(dòng)作方法。正如其名稱(chēng)所顯示的,它可以在給定的輔助標(biāo)簽?zāi)M在視圖上的觸擊。在大多數(shù)情況下,輔助標(biāo)簽和可視的文本標(biāo)簽(例如按鈕組件)是配套的。否則你就需要手動(dòng)設(shè)置輔助標(biāo)簽.
一些控件,諸如?UISwitch,更加復(fù)雜,需要比簡(jiǎn)單的觸擊更復(fù)雜的步驟來(lái)觸發(fā)。 KIF 提供了一個(gè)特殊的?setOn:forSwitchWithAccessibilityLabel:?方法來(lái)改變一個(gè)切換的狀態(tài).
總之,這個(gè)方法對(duì)測(cè)試動(dòng)作進(jìn)行了四步操作:
觸擊“Settings” 選項(xiàng)卡按鈕條.
將 “調(diào)試模式Debug Mode” 切換到它的“開(kāi)啟”狀態(tài).
觸擊 “Clear History” 按鈕.
在UIAlertView上觸擊“Clear”按鈕.
這步看起來(lái)很眼熟? 應(yīng)該是熟悉的! 它們就是之前章節(jié)中你手動(dòng)進(jìn)行的操作!
點(diǎn)擊Product Test菜單或者使用Command-U運(yùn)行測(cè)試。應(yīng)用開(kāi)始運(yùn)行,然后KIF接手,自動(dòng)開(kāi)啟調(diào)試模式并清除歷史記錄.
如果你啟用了通知,Xcode將告知你測(cè)試的狀態(tài):
有時(shí)候測(cè)試運(yùn)行器或者說(shuō)KIF可能會(huì)有一點(diǎn)挑剔,拒絕運(yùn)行測(cè)試,你只能看到一個(gè)空白的模擬器屏幕。如果遇到這樣的情況,那么:
清理一下項(xiàng)目(ProductClean)
構(gòu)建并運(yùn)行
等待應(yīng)用啟動(dòng)
在Xcode中終止應(yīng)用的運(yùn)行
這一過(guò)程確保了模擬器正在運(yùn)行,并且你正在最新的構(gòu)建上進(jìn)行操作。經(jīng)過(guò)上面的幾步之后,試試再一次運(yùn)行測(cè)試。問(wèn)題應(yīng)該會(huì)消失
如果問(wèn)題依然存在, 你可以參考?KIF 故障排除步驟。
現(xiàn)在你已經(jīng)在?beforeAll 中有了一個(gè)測(cè)試前置動(dòng)作, 是時(shí)候加入你的第一個(gè)測(cè)試了!
一個(gè)簡(jiǎn)單的測(cè)試:隨處點(diǎn)擊該應(yīng)用程序有一個(gè)標(biāo)準(zhǔn)的選項(xiàng)卡界面,三個(gè)選項(xiàng)卡中,每一個(gè)都使用了UINavigationController。先做個(gè)熱身吧,測(cè)試一下:
故事版被適當(dāng)?shù)剡B接起來(lái)
選項(xiàng)卡展示正確的視圖
選項(xiàng)欄按鈕自動(dòng)地設(shè)置成類(lèi)似文本標(biāo)簽這樣的輔助標(biāo)簽,所以KIF可以通過(guò)設(shè)置選項(xiàng)欄中的“歷史”、“定時(shí)器”、“設(shè)置”標(biāo)簽找到歷史、計(jì)時(shí)器和設(shè)置。
歷史選項(xiàng)卡有一個(gè)顯示所有定時(shí)器執(zhí)行任務(wù)的表視圖。從solanum組里打開(kāi)HistoryViewController.m,在viewDidLoad末尾處添加以下幾行:
[self.tableView setAccessibilityLabel:@"History List"]; [self.tableView setIsAccessibilityElement:YES];
這樣,表視圖的輔助標(biāo)簽就設(shè)置好了,KIF能夠找到它。通常情況下,一個(gè)表視圖僅在空的情況下才能被訪(fǎng)問(wèn)。表視圖的單元格有可能被當(dāng)做是一個(gè)目 標(biāo),所以表視圖它本身會(huì)被藏在輔助功能的API之下?;旧希o助功能的API在默認(rèn)情況下假定表視圖是不重要的。就輔助本身而言,這是合理的。不過(guò)如果你可能想在KIF中引用表視圖,那么可以使用setIsAccessibilityElement:,它確保表視圖始終是可訪(fǎng)問(wèn)的,不管其是否有內(nèi)容。
對(duì)于使用輔助功能的用戶(hù)來(lái)說(shuō),非空的表格視圖可以訪(fǎng)問(wèn)會(huì)使操作復(fù)雜化。在你的應(yīng)用中,你可以把相應(yīng)的代碼包裹在#ifdef DEBUG和#endif指令之間,這樣這些代碼就只會(huì)被編譯到調(diào)試構(gòu)建中。DEBUG預(yù)處理器宏已經(jīng)在Xcode項(xiàng)目模板中被提前定義好了。
Timer 選項(xiàng)卡有一些控件,而“任務(wù)名稱(chēng)”文本框放在了視圖的頂部,以方便用戶(hù)使用。你無(wú)需通過(guò)代碼來(lái)設(shè)定標(biāo)簽,只需可以打開(kāi)?Main.storyboard?并找到Timer View Controller視圖. 選擇task name text field#。
打開(kāi)Utilities,選擇Identity Inspector。提示:從左邊數(shù)第三個(gè)圖標(biāo), 或者使用快捷鍵``? ? 3`。
在inspector的Accessibility下,Label一欄中輸入Task Name。小心,這是大小寫(xiě)敏感的。確保輸入了大寫(xiě)的 T 和 N!
Settings 面板已經(jīng)設(shè)置好了視圖的輔助標(biāo)簽,你可以進(jìn)行下一步操作了!
在你自己的項(xiàng)目中,你將需要繼續(xù)填寫(xiě)輔助標(biāo)簽,你可以通過(guò)代碼,也可以通過(guò)上述 Interface Builder的方式。為了方便,示例應(yīng)用余下來(lái)的選項(xiàng)已經(jīng)設(shè)置好了。
回到?UITests.m, 在beforeAll之后加入這個(gè)方法:
- (void)test00TabBarButtons { // 1 [tester tapViewWithAccessibilityLabel:@"History"]; [tester waitForViewWithAccessibilityLabel:@"History List"]; // 2 [tester tapViewWithAccessibilityLabel:@"Timer"]; [tester waitForViewWithAccessibilityLabel:@"Task Name"]; // 3 [tester tapViewWithAccessibilityLabel:@"Settings"]; [tester waitForViewWithAccessibilityLabel:@"Debug Mode"]; }
測(cè)試運(yùn)行器會(huì)在運(yùn)行時(shí)尋找所有以test開(kāi)頭的方法, 然后按字母順序運(yùn)行它們。這個(gè)方法以test00開(kāi)頭,因此它會(huì)在你之后加入的方法之前運(yùn)行,那些是以諸如test10、test20這樣的名字開(kāi)頭的。
這些方法會(huì)進(jìn)行類(lèi)似一些動(dòng)作: 觸擊標(biāo)簽欄,檢查期望的視圖是否被顯示在屏幕上。waitForViewWithAccessibilityLabel:默認(rèn)的超時(shí)時(shí)間是10秒。如果在這段時(shí)間內(nèi)預(yù)期的東西沒(méi)有顯示,測(cè)試就宣告失敗。
點(diǎn)擊?ProductTest?或者 Command-U 運(yùn)行測(cè)試。你會(huì)注意到beforeAll中的步驟會(huì)清除掉歷史記錄,接著test00TabBarButtons會(huì)接管并按順序切換到歷史、定時(shí)器和設(shè)置標(biāo)簽。
哈哈,你看到了嗎?你只是編寫(xiě)并運(yùn)行了一個(gè)接口測(cè)試, 就會(huì)看到你的小應(yīng)用自己在“啟動(dòng)”了!恭喜了! 你正在掌握自動(dòng)化UI測(cè)試的旅途上。
用戶(hù)輸入當(dāng)然,切換標(biāo)簽很棒,不過(guò)我們也該關(guān)注一下更加真實(shí)的動(dòng)作:輸入文本,觸發(fā)模式對(duì)話(huà)框,以及選中表視圖中的一行。
測(cè)試應(yīng)用中內(nèi)置了更改工作時(shí)間、休息時(shí)間、推薦的重復(fù)次數(shù)的預(yù)設(shè)功能。如果你很好奇,想查看他們的定義,那么可以瀏覽一下PresetsViewController.m中的presetItems。
每個(gè)預(yù)設(shè)本身就可以成為一個(gè)測(cè)試。但是作為其他測(cè)試的一部分更有效。因此我們把它獨(dú)立出來(lái)成為一個(gè)輔助方法。
把以下的方法添加到UITests.m的實(shí)現(xiàn)模塊中去:
- (void)test00TabBarButtons { // 1 [tester tapViewWithAccessibilityLabel:@"History"]; [tester waitForViewWithAccessibilityLabel:@"History List"]; // 2 [tester tapViewWithAccessibilityLabel:@"Timer"]; [tester waitForViewWithAccessibilityLabel:@"Task Name"]; // 3 [tester tapViewWithAccessibilityLabel:@"Settings"]; [tester waitForViewWithAccessibilityLabel:@"Debug Mode"]; }
這里的第一步是切換到計(jì)時(shí)器標(biāo)簽,然后觸擊導(dǎo)航欄中的預(yù)設(shè)按鈕。當(dāng)“預(yù)設(shè)列表”表視圖出現(xiàn)的時(shí)候,觸擊指定的一行。
觸擊行會(huì)導(dǎo)致視圖控制器會(huì)消失,所以要用waitForAbsenceOfViewWithAccessibilityLabel:來(lái)保證它在你繼續(xù)下一步之前已經(jīng)消失了。
你注意到了嗎?這個(gè)方法不是以test為開(kāi)頭的。測(cè)試運(yùn)行器是不會(huì)自動(dòng)運(yùn)行它的。你需要在測(cè)試中手動(dòng)調(diào)用這個(gè)方法。
現(xiàn)在,添加以下測(cè)試方法到UITests.m:
- (void)test10PresetTimer { // 1 [tester tapViewWithAccessibilityLabel:@"Timer"]; // 2 [tester enterText:@"Set up a test" intoViewWithAccessibilityLabel:@"Task Name"]; [tester tapViewWithAccessibilityLabel:@"done"]; // 3 [self selectPresetAtIndex:1]; // 4 UISlider *slider = (UISlider *)[tester waitForViewWithAccessibilityLabel:@"Work Time Slider"]; STAssertEqualsWithAccuracy([slider value], 15.0f, 0.1, @"Work time slider was not set!"); }
KIF測(cè)試操作有可讀性很好的名字,看看你能不能發(fā)現(xiàn)接下來(lái)會(huì)發(fā)生什么?把它看成是一個(gè)……測(cè)試!沒(méi)錯(cuò),我故意用了雙關(guān)語(yǔ)。
好的,這就是實(shí)際發(fā)生的步驟:
切換到計(jì)時(shí)器標(biāo)簽。
?在“任務(wù)名”輔助標(biāo)簽的文本框中輸入“Set up a test”(記住,你之前添加了這個(gè)標(biāo)簽到面板中)。觸擊“Done”按鈕來(lái)關(guān)閉鍵盤(pán)。
請(qǐng)求輔助方法來(lái)選擇第二個(gè)預(yù)設(shè)。
選擇預(yù)設(shè)會(huì)改變滑動(dòng)器的值,所以要確保它確實(shí)更改為正確的值。
在代碼最后一部分,你會(huì)發(fā)現(xiàn)一個(gè)有用的技巧:waitForViewWithAccessibilityLabel:。它不僅僅可以等待視圖出現(xiàn),還可以返回一個(gè)指針到視圖中去。這樣,你可以將返回值類(lèi)型轉(zhuǎn)換成UISlider,以便匹配適當(dāng)?shù)念?lèi)型。
由于KIF測(cè)試用例也是常規(guī)的OCUnit測(cè)試用例,因此你可以調(diào)用標(biāo)準(zhǔn)的STAssert斷言宏。斷言是運(yùn)行時(shí)的檢查項(xiàng),如果某些條件不滿(mǎn)足,斷言會(huì)導(dǎo)致測(cè)試失敗。最簡(jiǎn)單的斷言是STAssertTrue,用來(lái)判斷傳入?yún)?shù)是否為true。
STAssertEquals會(huì)檢查傳入的前兩個(gè)參數(shù)是否相等。滾動(dòng)條的值是float類(lèi)型,所以要注意匹配他們的類(lèi)型。因此,在斷言里我們使用15.0f。此外,你還需要注意浮點(diǎn)數(shù)的誤差,這是因?yàn)楦↑c(diǎn)數(shù)的值不可能是100%精確的。比如說(shuō),15.0實(shí)際上可能被存儲(chǔ)為 15.000000000000001。所以STAssertEqualsWithAccuracy是一個(gè)更好的選擇,它的第三個(gè)參數(shù)指定偏差范圍。在這種情況下,如果該值的誤差在-0.1到+0.1之間,斷言仍然能通過(guò)。
用Command-U運(yùn)行測(cè)試。你會(huì)看到這三個(gè)序列:beforeAll清除歷史記錄,test00TabBarButtons在每個(gè)標(biāo)簽間切換,最后的是test10PresetTimer,輸入一個(gè)任務(wù)名并選擇一個(gè)預(yù)設(shè)。
這是另一個(gè)成功的測(cè)試用例!此時(shí),你的測(cè)試模仿用戶(hù)的觸擊,甚至鍵盤(pán)輸入,但是還有更多驚喜在后頭!
啟動(dòng)定時(shí)器下面給出一個(gè)例子,定時(shí)循環(huán)跑一個(gè)應(yīng)用程序,應(yīng)用的用戶(hù)可能選擇:工作8分鐘,休息2分鐘,工作8分鐘,休息2分鐘,最后工作8分鐘。接下來(lái)是一個(gè)較長(zhǎng)的休息。然后再重新啟動(dòng)應(yīng)用程序。
以上例子的參數(shù)是:
工作時(shí)間:8分鐘
休息時(shí)間:2分鐘
重復(fù)次數(shù):3
接下來(lái)的KIF測(cè)試需要輸入這些參數(shù),然后點(diǎn)擊“開(kāi)始工作”按鈕來(lái)啟動(dòng)定時(shí)器。添加以下的方法到UITests.m,直接加在你之前添加的測(cè)試之后:
- (void)test20StartTimerAndWaitForFinish { [tester tapViewWithAccessibilityLabel:@"Timer"]; [tester clearTextFromAndThenEnterText:@"Test the timer" intoViewWithAccessibilityLabel:@"Task Name"]; [tester tapViewWithAccessibilityLabel:@"done"]; [tester setValue:1 forSliderWithAccessibilityLabel:@"Work Time Slider"]; [tester setValue:50 forSliderWithAccessibilityLabel:@"Work Time Slider"]; [tester setValue:1 forSliderWithAccessibilityLabel:@"Work Time Slider"]; [tester setValue:8 forSliderWithAccessibilityLabel:@"Work Time Slider"]; [tester setValue:1 forSliderWithAccessibilityLabel:@"Break Time Slider"]; [tester setValue:25 forSliderWithAccessibilityLabel:@"Break Time Slider"]; [tester setValue:2 forSliderWithAccessibilityLabel:@"Break Time Slider"]; }
因?yàn)檫@個(gè)測(cè)試會(huì)在test10PresetTimer(這個(gè)測(cè)試會(huì)設(shè)置任務(wù)名)后立刻運(yùn)行,你可以使用clearTextFromAndThenEnterText:intoViewWithAccessibilityLabel:,而不是使用enterText:intoViewWithAccessibilityLabel:清除任何現(xiàn)存的文本。
最后,這里有幾個(gè)setValue:forSliderWithAccessibilityLabel:調(diào)用。這是UISlider指定的給新變量賦值的方法。注意,并不總是很精確。KIF實(shí)際上是模擬觸擊事件去設(shè)置新的值,有時(shí)候像素的計(jì)算會(huì)有所偏差。不過(guò)這沒(méi)什么大問(wèn)題,因?yàn)槲覀兊氖种敢膊皇翘貏e精確!
你僅需要設(shè)置每個(gè)滾動(dòng)條的值一次。多次的調(diào)用僅僅是為了反復(fù),所以你會(huì)看到KIF不停地更改值。
使用Command-U運(yùn)行測(cè)試。
剩下的UI測(cè)試是在UIStepper控制器中設(shè)置重復(fù)的次數(shù),還有“開(kāi)始工作”按鈕?!伴_(kāi)始工作”按鈕很簡(jiǎn)單——你可以使用tapViewWithAccessibilityLabel:模擬觸擊。但對(duì)于UIStepper來(lái)說(shuō),我們需要采用一些迂回策略。
定制觸擊如下圖所示,UIStepper控制器分為兩部分,因此,如果你僅僅調(diào)用tapViewWithAccessibilityLabel:,很難確定會(huì)發(fā)生什么。
KIF會(huì)開(kāi)始嘗試觸擊控制器的中心。如果它是一個(gè)不可觸擊的區(qū)域,它會(huì)接下來(lái)嘗試點(diǎn)擊左上角、右上角、左下角、右下角。結(jié)果顯示,觸擊+、-之前的分界線(xiàn)會(huì)觸發(fā)+,所以它會(huì)不斷地增加重復(fù)次數(shù)。
但是如果你想減少次數(shù)呢?有一些變通的方法的,例如深入子視圖找到減號(hào)的按鈕,另一種方法則是使用KIF的tapScreenAtPoint:測(cè)試操作,可以模擬觸擊屏幕上的任意點(diǎn)。
你對(duì)CGGeometry了解得如何?如何計(jì)算出+/-按鈕在窗口中的坐標(biāo)?準(zhǔn)備好來(lái)證明你的實(shí)力沒(méi)?為什么不接受這個(gè)挑戰(zhàn)呢?這完全是可選的, 你可以直接跳到下面去查看代碼和計(jì)算。但是,如果想測(cè)驗(yàn)一下你的技能,可以嘗試在偷看答案之前先自己寫(xiě)一下代碼。
請(qǐng)注意,你很快需要將下面的代碼添加到測(cè)試中,這個(gè)任務(wù)只是測(cè)試你的數(shù)學(xué)和CGGeometry能力。
首先,你需要引用UIStepper:
UIStepper *repsStepper = (UIStepper*)[tester waitForViewWithAccessibilityLabel:@"Reps Stepper"];
然后你可以找到中心點(diǎn):
CGPoint stepperCenter = [repsStepper.window convertPoint:repsStepper.center fromView:repsStepper.superview];
UIStepper可以是在嵌套視圖內(nèi),而你真正想要的是UIStepper的中心點(diǎn)相對(duì)于整個(gè)窗口的坐標(biāo),因此需要調(diào)用convertPoint:fromView:。
現(xiàn)在,你得到了中心點(diǎn)相對(duì)于窗口的位置,你可以減少x軸的值來(lái)獲取到減號(hào)的按鈕,或者增加x軸來(lái)獲取加號(hào)的按鈕。
CGPoint minusButton = stepperCenter; minusButton.x -= CGRectGetWidth(repsStepper.frame) / 4; CGPoint plusButton = stepperCenter; plusButton.x += CGRectGetWidth(repsStepper.frame) / 4;
如果增加或者減少UIStepper寬度的1/4,就會(huì)正好到達(dá)加號(hào)、減號(hào)按鈕的中間位置。
瞧!這兩個(gè)點(diǎn),相當(dāng)于UIStepper控制器中的加號(hào)/減號(hào)按鈕的中心點(diǎn)。
觸擊屏幕的指定點(diǎn)是萬(wàn)不得已的方法,但有時(shí)它是測(cè)試UI的唯一的方法。例如,你也許會(huì)有一個(gè)自行定制的控制器沒(méi)有實(shí)現(xiàn)UIAccessibility Protocol。
定時(shí)器收官好的,你已經(jīng)到了定時(shí)器測(cè)試的最后一步了。你開(kāi)始意識(shí)到用KIF做UI測(cè)試的潛力了么?很好!
最后的步驟是設(shè)置重復(fù)次數(shù),然后啟動(dòng)定時(shí)器。添加以下代碼到UITests.m中的test20StartTimerAndWaitForFinish的結(jié)尾:
// 1 UIStepper *repsStepper = (UIStepper*)[tester waitForViewWithAccessibilityLabel:@"Reps Stepper"]; CGPoint stepperCenter = [repsStepper.window convertPoint:repsStepper.center fromView:repsStepper.superview]; CGPoint minusButton = stepperCenter; minusButton.x -= CGRectGetWidth(repsStepper.frame) / 4; CGPoint plusButton = stepperCenter; plusButton.x += CGRectGetWidth(repsStepper.frame) / 4; // 2 [tester waitForTimeInterval:1]; // 3 for (int i = 0; i < 20; i++) { [tester tapScreenAtPoint:minusButton]; } [tester waitForTimeInterval:1]; [tester tapScreenAtPoint:plusButton]; [tester waitForTimeInterval:1]; [tester tapScreenAtPoint:plusButton]; [tester waitForTimeInterval:1]; // 4 [KIFUITestActor setDefaultTimeout:60]; // 5 [tester tapViewWithAccessibilityLabel:@"Start Working"]; // the timer is ticking away in the modal view... [tester waitForViewWithAccessibilityLabel:@"Start Working"]; // 6 [KIFUITestActor setDefaultTimeout:10]
這里介紹在最后階段中即將會(huì)發(fā)生什么:
上面的代碼尋找UIStepper的加號(hào)和減號(hào)按鈕的坐標(biāo)。
waitForTimeInterval:調(diào)用增加了延時(shí),這樣你可以看到步數(shù)值的變化——否則的話(huà),對(duì)于肉眼而言,它的變化太快了。
這個(gè)步數(shù)的最大值是20,所以觸擊減號(hào)按鈕20次會(huì)把值恢復(fù)到1。然后觸擊加號(hào)按鈕2次(以最小的延時(shí)交替)把重復(fù)數(shù)字變?yōu)槠谕闹?3。
每個(gè)測(cè)試步驟默認(rèn)的超時(shí)時(shí)間是10秒。即使在加速調(diào)試模式下,有可能20多分鐘長(zhǎng)度的“工作”也需要超過(guò)10秒鐘時(shí)間完成,所以需要設(shè)置一個(gè)比較寬松的超時(shí)時(shí)間——60秒。
觸擊“開(kāi)始工作”按鈕,它會(huì)彈出模式視圖控制器。當(dāng)重復(fù)都結(jié)束后,模式視圖控制器會(huì)消失。這意味著你將返回定時(shí)器視圖控制器上,所以 等待“開(kāi)始工作”按鈕再次出現(xiàn)其實(shí)是在等待模式視圖控制器消失。
重設(shè)超時(shí)時(shí)間為10秒。
一旦你保存文件,你會(huì)發(fā)現(xiàn)有方法聲明一側(cè)會(huì)出現(xiàn)一個(gè)小的棱形圖標(biāo):
這是一個(gè)運(yùn)行單個(gè)測(cè)試的按鈕。因此,你可以在測(cè)試環(huán)境下只運(yùn)行這個(gè)方法,而不需要運(yùn)行整套測(cè)試用例。優(yōu)雅!點(diǎn)擊菱形按鈕來(lái)運(yùn)行這個(gè)測(cè)試,你會(huì)看到模擬器啟動(dòng),并運(yùn)行測(cè)試用例。只要坐在一旁觀(guān)看它輸入任務(wù)名,滑動(dòng)滾動(dòng)條,以及定時(shí)器的時(shí)間流動(dòng)就可以了。不用動(dòng)一根手指,你就能測(cè)試UI了。
成功了!現(xiàn)在你可以成功設(shè)置測(cè)試,幫助你完成一系列UI控制器的操作。
如果你通過(guò)菱形按鈕跑單個(gè)測(cè)試,它只跑一個(gè)簡(jiǎn)單的方法而不會(huì)在開(kāi)頭調(diào)用beforeAll。如果你的測(cè)試是依賴(lài)于beforeAll的,你仍然需要跑整套完整的測(cè)試。
提前結(jié)束“去工作!”模式視圖控制器中有一個(gè)放棄按鈕可以讓用戶(hù)取消一個(gè)時(shí)間周期。你仍可以測(cè)量已經(jīng)工作的分鐘,即使很早就結(jié)束了測(cè)試。這個(gè)數(shù)據(jù)仍然會(huì)被記錄到歷史上,但會(huì)被標(biāo)記為整個(gè)周期沒(méi)有跑完。
你不用相信我的話(huà),可以自己嘗試一下。只需做一些類(lèi)似上述測(cè)試的東西。設(shè)置定時(shí)器的參數(shù),觸擊*開(kāi)始工作按鈕,然后觸擊放棄按鈕。
不要馬上觸擊“放棄”按鈕——定時(shí)器需要運(yùn)作一段時(shí)間,應(yīng)用程序才開(kāi)始創(chuàng)建歷史記錄。所以,你可以將鼠標(biāo)移動(dòng)到測(cè)試上,手動(dòng)停止測(cè)試?;蛘?,你可以編程設(shè)定它在你選定的時(shí)間和位置。如果你生性猶豫,你可以跳過(guò)這個(gè)挑戰(zhàn)。然而,如果你喜歡編寫(xiě)應(yīng)用去幫你處理各種雜事,就試一下吧!你知道如何在“開(kāi)始工作”和“放棄工作”之間增加一點(diǎn)延時(shí)么?
你可以調(diào)用waitForTimeInterval:在你的測(cè)試?yán)镌黾友訒r(shí)。
在UITests.m中添加下列的測(cè)試方法,加在其他測(cè)試的下面:
- (void)test30StartTimerAndGiveUp { [tester tapViewWithAccessibilityLabel:@"Timer"]; [tester clearTextFromAndThenEnterText:@"Give Up" intoViewWithAccessibilityLabel:@"Task Name"]; [tester tapViewWithAccessibilityLabel:@"done"]; [self selectPresetAtIndex:2]; [tester tapViewWithAccessibilityLabel:@"Start Working"]; [tester waitForTimeInterval:3]; [tester tapViewWithAccessibilityLabel:@"Give Up"]; [[tester usingTimeout:1] waitForViewWithAccessibilityLabel:@"Start Working"]; }
在確保你位于正確的選項(xiàng)卡之后,設(shè)置任務(wù)名然后選擇好預(yù)設(shè)。然后啟動(dòng)定時(shí)器,在放棄之前等待3秒。
方法的最后一行等待模式視圖控制器消失,返回主界面。記住,默認(rèn)的超時(shí)時(shí)間是10秒,但是實(shí)際上不需要這么長(zhǎng)時(shí)間——觸擊“放棄”按鈕,模式視圖控制器會(huì)立即消失。
在之前的測(cè)試中,你使用了類(lèi)方法setDefaultTimeout:為所有測(cè)試操作設(shè)置全局的超時(shí)時(shí)間。在這里,你可以調(diào)用usingTimeout:為某一步定制的超時(shí)時(shí)間。
保存文件并點(diǎn)擊菱形按鈕運(yùn)行單個(gè)測(cè)試。當(dāng)定時(shí)器開(kāi)始的時(shí)候,你可以發(fā)現(xiàn)它在放棄、返回主界面之前會(huì)等待3秒。
歷史和拖動(dòng)之前我們沒(méi)有怎么關(guān)注歷史選項(xiàng)卡,現(xiàn)在該輪到它出一下風(fēng)頭了。如果你按照練習(xí)一步一步來(lái)進(jìn)行操作的話(huà),那么你至少會(huì)有一條歷史記錄。構(gòu)建并運(yùn)行該應(yīng)用,然后切換到歷史選項(xiàng)卡。
歷史表視圖實(shí)現(xiàn)了最新的iOS7刪除手勢(shì)——當(dāng)你把整行往左邊拖動(dòng),然后觸擊出現(xiàn)的“刪除”按鈕。這是你的下一個(gè)測(cè)試!這要求歷史中至少有一條記錄,所以你跑單個(gè)測(cè)試的時(shí)候要小心。你必須保證歷史記錄中有內(nèi)容,否則就沒(méi)什么可以測(cè)的!安全起見(jiàn),你需要完整地運(yùn)行整套測(cè)試,因?yàn)榍懊娴臏y(cè)試會(huì)創(chuàng)建一些歷史記錄供你使用。記住,測(cè)試的運(yùn)行順序是根據(jù)字母順序排列的,所以你可以很放心,前面的測(cè)試會(huì)創(chuàng)建一些歷史記錄的。
如果你看一下HistoryViewController.m中的tableView:cellForRowAtIndexPath:,你會(huì)發(fā)現(xiàn)每一格都能得到一個(gè)輔助標(biāo)簽,比如“第3行第0個(gè)”之類(lèi)的。這有助于KIF找到這一行。但在真實(shí)環(huán)境下這種標(biāo)簽很糟,所以不要在真正的應(yīng)用中使用。在示例工程中,我們使用#ifdef來(lái)確保這些只出現(xiàn)在調(diào)試構(gòu)建中。你應(yīng)該在你自己的應(yīng)用中做類(lèi)似的事情。請(qǐng)確保在發(fā)布構(gòu)建的時(shí)候,將輔助標(biāo)簽設(shè)為一些有用的內(nèi)容,切實(shí)為用戶(hù)提供輔助功能。
現(xiàn)在,打開(kāi)UITests.m并添加如下的測(cè)試方法:
- (void)test30StartTimerAndGiveUp { [tester tapViewWithAccessibilityLabel:@"Timer"]; [tester clearTextFromAndThenEnterText:@"Give Up" intoViewWithAccessibilityLabel:@"Task Name"]; [tester tapViewWithAccessibilityLabel:@"done"]; [self selectPresetAtIndex:2]; [tester tapViewWithAccessibilityLabel:@"Start Working"]; [tester waitForTimeInterval:3]; [tester tapViewWithAccessibilityLabel:@"Give Up"]; [[tester usingTimeout:1] waitForViewWithAccessibilityLabel:@"Start Working"]; }
現(xiàn)在介紹上面方法的作用:
切換到“歷史”選項(xiàng)卡。
引用表視圖并追蹤總共存在多少行。如果一行數(shù)據(jù)有沒(méi)有,測(cè)試就會(huì)失敗。
往左拖動(dòng)表視圖的單元格。當(dāng)刪除按鈕出現(xiàn)后,觸擊它。
刪除一個(gè)單元格的時(shí)候,表視圖會(huì)有一個(gè)動(dòng)畫(huà)效果,所以在進(jìn)行下一步前添加一個(gè)短暫的延時(shí)。再次檢查表視圖的行數(shù),應(yīng)當(dāng)比之前的行數(shù)少一行。
用Command-U運(yùn)行整套測(cè)試,所有的測(cè)試會(huì)順序執(zhí)行。
現(xiàn)在,你的測(cè)試覆蓋了整個(gè)應(yīng)用的基本流程了——從數(shù)據(jù)重置到多次運(yùn)行定時(shí)器,再到驗(yàn)證和刪除歷史記錄。
如果你要繼續(xù)開(kāi)發(fā)這個(gè)應(yīng)用程序,這些測(cè)試會(huì)是一個(gè)很好的基礎(chǔ),確保界面沒(méi)有退步。這意味著在主界面變化的時(shí)候你需要更新你的測(cè)試——測(cè)試就像是你應(yīng)用的規(guī)格說(shuō)明,需要及時(shí)更新才能發(fā)揮作用。
進(jìn)一步到目前為止,你應(yīng)該對(duì)KIF的可能性有很好的了解,腦子里也應(yīng)該有不少主意,如何利用這個(gè)高效的功能測(cè)試工具來(lái)測(cè)試你自己的應(yīng)用程序。添加KIF到你自己的項(xiàng)目的時(shí)候,你可以查看這個(gè)文檔。你可以手動(dòng)添加KIF或者使用非常方便的CocoaPods依賴(lài)管理。
KIF是一個(gè)開(kāi)源項(xiàng)目,且有許多新功能還在不斷開(kāi)發(fā)之中。例如,在寫(xiě)這篇教程的時(shí)候,下一個(gè)KIF的發(fā)布版將會(huì)包含截屏的功能。這意味著可以在跑測(cè)試的時(shí)候通過(guò)屏幕截圖來(lái)查看全過(guò)程中的關(guān)鍵點(diǎn)。聽(tīng)起來(lái)這不是比用肉眼觀(guān)察KIF點(diǎn)擊和拖動(dòng)整個(gè)過(guò)程好上千萬(wàn)倍么?KIF正變得越來(lái)越好了,所以學(xué)習(xí)如何使用它,是一個(gè)很好的自我投資。
最后,由于KIF測(cè)試用例是OCUnit的子類(lèi),并在標(biāo)準(zhǔn)的Xcode5測(cè)試框架下運(yùn)行,你可以使用持續(xù)集成來(lái)跑這些測(cè)試。當(dāng)你干別的事情的時(shí)候,你擁有了一個(gè)能夠像人的手指一樣觸控的機(jī)器人去測(cè)試你的應(yīng)用程序。太棒了!
你可以在這里下載整個(gè)示例項(xiàng)目的代碼。測(cè)試快樂(lè)!
原文 iOS UI Testing with KIF
翻譯 SegmentFault
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/8703.html
Abstract There is an article shows demo code for making XMLSignature by using Java XML Digital Signature API, where it actually uses org.jcp.xml.dsig.internal.dom.XMLDSigRI to do DOM formation, and th...
閱讀 3275·2023-04-25 22:47
閱讀 3789·2021-10-11 10:59
閱讀 2317·2021-09-07 10:12
閱讀 4273·2021-08-11 11:15
閱讀 3442·2019-08-30 13:15
閱讀 1759·2019-08-30 13:00
閱讀 979·2019-08-29 14:02
閱讀 1698·2019-08-26 13:57