使用KIF 進行 iOS UI 測試

用戶期待iOS應用的高水準用戶體驗,於是你須要設計、開發和測試你的應用來知足這一不斷上升的指望。爲了達成這個目標,你會投入多少時間進行原始人工的用戶界面測試?你知道這活兒怎麼幹…從Xcode啓動你的應用,並不斷的用手指點擊同一些按鈕來確保你的設計中沒有退步。相比這些,你固然更願意作其它一些事情?html

KIF

考慮下使用Xcode 5中加強的UI測試,還有 OS X Server中支持的持續集成 這篇文章展現了蘋果公司爲開發者貢獻的最好的工具。ios

你也許會說,這很棒,可是如何讓那些簡單的用戶動做測試變得自動化呢,例如確保一個在合適區域的雙擊或觸摸後會進入正確的視圖?即便是測試腳本和機器人也不會有能在屏幕上滑動的電容式觸摸手指…呃…它們有手指麼?git

在這篇教程中,你將會學習到一些有關 KIF 的東西(Keep it Functional), 它是一個開源的用戶界面測試框架. 使用 KIF, 並利用 iOS中的輔助功能 API, 你將可以編寫模擬用戶輸入(例如點擊、觸摸和文本輸入)的測試。github

它提供自動化的、真實的用戶界面操做, 幫助放鬆你的心情,於是你就能夠只去關注本身的殺手級應用了,而不是在UI測試上耗費你職業生涯的一半時間。編程

讓咱們開始測試工做吧!segmentfault

入門

咱們以一個叫作茄薯Solanum(一個馬鈴薯品種的名字)的計時器應用爲例。這是一個基於番茄工做法的應用。xcode

它是這麼運做的:瀏覽器

  • 按照設定好的時間工做,接着休息一下,而後重複工做和休息.
  • 通過這樣若干個循環以後, 你就會休息一段更長的時間.

這個應用就是一個可以持續跟蹤時間區段的簡單計時器。安全

這款應用以後能使你的開發工做變得更加高效!app

從 這裏 下載並解壓縮入門項目.注意 KIF 是一個獨立的項目,它是爲茄薯應用構建一個用於測試目標的庫。所以你須要雙擊 solanum.xcworkspace 以便在Xcode中打開項目,而不是 solanum.xcodeproj。在項目導航視窗中找到這兩個項目,以下圖所示:

將應用的目標設置到solanum,而後選擇3.5或者4英寸的iPhone模擬器目標。不要使用64位,由於KIF在64位模擬器上有一些問題。構建並運行這個應用。四處瞧瞧,瞭解一下,而後切換到Settings。

應用有一個可以加速時間流逝的調試模式。所以你能夠設置一個20分鐘的計時器,測試時花10秒完成這個過程。這只是爲了幫助你測試應用,你應該不會想花20分鐘等着看它運行吧。

開啓調試模式來加速計時器,接着點擊Clear History按鈕,而後在確認彈出視圖上點擊Clear。這幾步確保了你是在一個乾淨的環境中開始測試做業。返回到Xcode並把應用停下來.

測試前操做

在項目瀏覽器中,展開solanum項目。 右擊UI測試文件夾並點擊New File…來加入你的新測試用例。

選擇 iOS\Cocoa Touch\Objective-C class 並點擊 Next, 將類命名爲 UITests 並使其成爲 KIFTestCase 的子類。

點擊Next並確保文件已經被添加到UI Tests目標中, 而不是 solanum 目標。 而後點擊接下來Create保存文件.

KIFTestCase是SenTestCase的一個子類. 那意味着你擁有了大部分的標準 OCUnit 測試方法和機制可使用,若是你已經很熟悉單元測試的話。

打開UITests.m,並在@implementation 一行添加以下的方法:

- (void)beforeAll {
  [tester tapViewWithAccessibilityLabel:@"Settings"];

  [tester setOn:YES forSwitchWithAccessibilityLabel:@"Debug Mode"];

  [tester tapViewWithAccessibilityLabel:@"Clear History"];
  [tester tapViewWithAccessibilityLabel:@"Clear"];
}

beforeAll 是一個在全部測試運行以前被調用一次的特殊方法。你能夠爲你這裏運行的測試設置任何實體變量和初始化條件.

tester 對象是指定的KIFUITestActor 類的一個縮略名稱。這個類包含了模擬用戶動做的方法,包括觸摸和滑動.

tapViewWithAccessibilityLabel 這也許是最常被用到的測試動做方法。正如其名稱所顯示的,它能夠在給定的輔助標籤模擬在視圖上的觸擊。在大多數狀況下,輔助標籤和可視的文本標籤(例如按鈕組件)是配套的。不然你就須要手動設置輔助標籤.

一些控件,諸如 UISwitch,更加複雜,須要比簡單的觸擊更復雜的步驟來觸發。 KIF 提供了一個特殊的 setOn:forSwitchWithAccessibilityLabel: 方法來改變一個切換的狀態.

總之,這個方法對測試動做進行了四步操做:

  • 觸擊「Settings」 選項卡按鈕條.
  • 將 「調試模式Debug Mode」 切換到它的「開啓」狀態.
  • 觸擊 「Clear History」 按鈕.
  • 在UIAlertView上觸擊「Clear」按鈕.

這步看起來很眼熟? 應該是熟悉的! 它們就是以前章節中你手動進行的操做!

點擊Product Test菜單或者使用Command-U運行測試。應用開始運行,而後KIF接手,自動開啓調試模式並清除歷史記錄.

若是你啓用了通知,Xcode將告知你測試的狀態:

有時候測試運行器或者說KIF可能會有一點挑剔,拒絕運行測試,你只能看到一個空白的模擬器屏幕。若是遇到這樣的狀況,那麼:

  • 清理一下項目(Product\Clean)
  • 構建並運行
  • 等待應用啓動
  • 在Xcode中終止應用的運行

這一過程確保了模擬器正在運行,而且你正在最新的構建上進行操做。通過上面的幾步以後,試試再一次運行測試。問題應該會消失

若是問題依然存在, 你能夠參考 KIF 故障排除步驟

如今你已經在 beforeAll 中有了一個測試前置動做, 是時候加入你的第一個測試了!

一個簡單的測試:隨處點擊

該應用程序有一個標準的選項卡界面,三個選項卡中,每個都使用了UINavigationController。先作個熱身吧,測試一下:

  • 故事版被適當地鏈接起來
  • 選項卡展現正確的視圖

選項欄按鈕自動地設置成相似文本標籤這樣的輔助標籤,因此KIF能夠經過設置選項欄中的「歷史」、「定時器」、「設置」標籤找到歷史、計時器和設置。

歷史選項卡有一個顯示全部定時器執行任務的表視圖。從solanum組裏打開HistoryViewController.m,在viewDidLoad末尾處添加如下幾行:

[self.tableView setAccessibilityLabel:@"History List"];
[self.tableView setIsAccessibilityElement:YES];

這樣,表視圖的輔助標籤就設置好了,KIF可以找到它。一般狀況下,一個表視圖僅在空的狀況下才能被訪問。表視圖的單元格有可能被當作是一個目 標,因此表視圖它自己會被藏在輔助功能的API之下。基本上,輔助功能的API在默認狀況下假定表視圖是不重要的。就輔助自己而言,這是合理的。不過若是你可能想在KIF中引用表視圖,那麼可使用setIsAccessibilityElement:,它確保表視圖始終是可訪問的,無論其是否有內容。

對於使用輔助功能的用戶來講,非空的表格視圖能夠訪問會使操做複雜化。在你的應用中,你能夠把相應的代碼包裹在#ifdef DEBUG#endif指令之間,這樣這些代碼就只會被編譯到調試構建中。DEBUG預處理器宏已經在Xcode項目模板中被提早定義好了。

Timer 選項卡有一些控件,而「任務名稱」文本框放在了視圖的頂部,以方便用戶使用。你無需經過代碼來設定標籤,只需能夠打開 Main.storyboard 並找到Timer View Controller視圖. 選擇task name text field#。

打開Utilities,選擇Identity Inspector。提示:從左邊數第三個圖標, 或者使用快捷鍵``⌥ ⌘ 3`。

在inspector的Accessibility下,Label一欄中輸入Task Name。當心,這是大小寫敏感的。確保輸入了大寫的 T 和 N!

Settings 面板已經設置好了視圖的輔助標籤,你能夠進行下一步操做了!

在你本身的項目中,你將須要繼續填寫輔助標籤,你能夠經過代碼,也能夠經過上述 Interface Builder的方式。爲了方便,示例應用餘下來的選項已經設置好了。

回到 UITests.m, 在beforeAll以後加入這個方法:

- (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"];
    }

測試運行器會在運行時尋找全部以test開頭的方法, 而後按字母順序運行它們。這個方法以test00開頭,所以它會在你以後加入的方法以前運行,那些是以諸如test10test20這樣的名字開頭的。

這些方法會進行相似一些動做: 觸擊標籤欄,檢查指望的視圖是否被顯示在屏幕上。waitForViewWithAccessibilityLabel:默認的超時時間是10秒。若是在這段時間內預期的東西沒有顯示,測試就宣告失敗。

點擊 Product\Test 或者 Command-U 運行測試。你會注意到beforeAll中的步驟會清除掉歷史記錄,接着test00TabBarButtons會接管並按順序切換到歷史、定時器和設置標籤。

哈哈,你看到了嗎?你只是編寫並運行了一個接口測試, 就會看到你的小應用本身在「啓動」了!恭喜了! 你正在掌握自動化UI測試的旅途上。

用戶輸入

固然,切換標籤很棒,不過咱們也該關注一下更加真實的動做:輸入文本,觸發模式對話框,以及選中表視圖中的一行。

測試應用中內置了更改工做時間、休息時間、推薦的重複次數的預設功能。若是你很好奇,想查看他們的定義,那麼能夠瀏覽一下PresetsViewController.m中的presetItems

每一個預設自己就能夠成爲一個測試。可是做爲其餘測試的一部分更有效。所以咱們把它獨立出來成爲一個輔助方法。

把如下的方法添加到UITests.m的實現模塊中去:

- (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"];
}

這裏的第一步是切換到計時器標籤,而後觸擊導航欄中的預設按鈕。當「預設列表」表視圖出現的時候,觸擊指定的一行。

觸擊行會致使視圖控制器會消失,因此要用waitForAbsenceOfViewWithAccessibilityLabel:來保證它在你繼續下一步以前已經消失了。

你注意到了嗎?這個方法不是以test爲開頭的。測試運行器是不會自動運行它的。你須要在測試中手動調用這個方法。

如今,添加如下測試方法到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測試操做有可讀性很好的名字,看看你能不能發現接下來會發生什麼?把它當作是一個……測試!沒錯,我故意用了雙關語。

好的,這就是實際發生的步驟:

  1. 切換到計時器標籤。
  2.  在「任務名」輔助標籤的文本框中輸入「Set up a test」(記住,你以前添加了這個標籤到面板中)。觸擊「Done」按鈕來關閉鍵盤。
  3. 請求輔助方法來選擇第二個預設。
  4. 選擇預設會改變滑動器的值,因此要確保它確實更改成正確的值。

在代碼最後一部分,你會發現一個有用的技巧:waitForViewWithAccessibilityLabel:。它不只僅能夠等待視圖出現,還能夠返回一個指針到視圖中去。這樣,你能夠將返回值類型轉換成UISlider,以便匹配適當的類型。

因爲KIF測試用例也是常規的OCUnit測試用例,所以你能夠調用標準的STAssert斷言宏。斷言是運行時的檢查項,若是某些條件不知足,斷言會致使測試失敗。最簡單的斷言是STAssertTrue,用來判斷傳入參數是否爲true。

STAssertEquals會檢查傳入的前兩個參數是否相等。滾動條的值是float類型,因此要注意匹配他們的類型。所以,在斷言裏咱們使用15.0f。此外,你還須要注意浮點數的偏差,這是由於浮點數的值不多是100%精確的。好比說,15.0實際上可能被存儲爲 15.000000000000001。因此STAssertEqualsWithAccuracy是一個更好的選擇,它的第三個參數指定誤差範圍。在這種狀況下,若是該值的偏差在-0.1到+0.1之間,斷言仍然能經過。

用Command-U運行測試。你會看到這三個序列:beforeAll清除歷史記錄,test00TabBarButtons在每一個標籤間切換,最後的是test10PresetTimer,輸入一個任務名並選擇一個預設。

這是另外一個成功的測試用例!此時,你的測試模仿用戶的觸擊,甚至鍵盤輸入,可是還有更多驚喜在後頭!

啓動定時器

下面給出一個例子,定時循環跑一個應用程序,應用的用戶可能選擇:工做8分鐘,休息2分鐘,工做8分鐘,休息2分鐘,最後工做8分鐘。接下來是一個較長的休息。而後再從新啓動應用程序。

以上例子的參數是:

  • 工做時間:8分鐘
  • 休息時間:2分鐘
  • 重複次數:3

接下來的KIF測試須要輸入這些參數,而後點擊「開始工做」按鈕來啓動定時器。添加如下的方法到UITests.m,直接加在你以前添加的測試以後:

- (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"];
}

由於這個測試會在test10PresetTimer(這個測試會設置任務名)後馬上運行,你可使用clearTextFromAndThenEnterText:intoViewWithAccessibilityLabel:,而不是使用enterText:intoViewWithAccessibilityLabel:清除任何現存的文本。

最後,這裏有幾個setValue:forSliderWithAccessibilityLabel:調用。這是UISlider指定的給新變量賦值的方法。注意,並不老是很精確。KIF其實是模擬觸擊事件去設置新的值,有時候像素的計算會有所誤差。不過這沒什麼大問題,由於咱們的手指也不是特別精確!

你僅須要設置每一個滾動條的值一次。屢次的調用僅僅是爲了反覆,因此你會看到KIF不停地更改值。

使用Command-U運行測試。

剩下的UI測試是在UIStepper控制器中設置重複的次數,還有「開始工做」按鈕。「開始工做」按鈕很簡單——你可使用tapViewWithAccessibilityLabel:模擬觸擊。但對於UIStepper來講,咱們須要採用一些迂迴策略。

定製觸擊

以下圖所示,UIStepper控制器分爲兩部分,所以,若是你僅僅調用tapViewWithAccessibilityLabel:,很難肯定會發生什麼。

KIF會開始嘗試觸擊控制器的中心。若是它是一個不可觸擊的區域,它會接下來嘗試點擊左上角、右上角、左下角、右下角。結果顯示,觸擊+、-以前的分界線會觸發+,因此它會不斷地增長重複次數。

可是若是你想減小次數呢?有一些變通的方法的,例如深刻子視圖找到減號的按鈕,另外一種方法則是使用KIF的tapScreenAtPoint:測試操做,能夠模擬觸擊屏幕上的任意點。

你對CGGeometry瞭解得如何?如何計算出+/-按鈕在窗口中的座標?準備好來證實你的實力沒?爲何不接受這個挑戰呢?這徹底是可選的, 你能夠直接跳到下面去查看代碼和計算。可是,若是想測驗一下你的技能,能夠嘗試在偷看答案以前先本身寫一下代碼。

請注意,你很快須要將下面的代碼添加到測試中,這個任務只是測試你的數學和CGGeometry能力。

UIStepper Geometry

首先,你須要引用UIStepper:

UIStepper *repsStepper = (UIStepper*)[tester waitForViewWithAccessibilityLabel:@"Reps Stepper"];

而後你能夠找到中心點:

CGPoint stepperCenter = [repsStepper.window convertPoint:repsStepper.center
fromView:repsStepper.superview];

UIStepper能夠是在嵌套視圖內,而你真正想要的是UIStepper的中心點相對於整個窗口的座標,所以須要調用convertPoint:fromView:

如今,你獲得了中心點相對於窗口的位置,你能夠減小x軸的值來獲取到減號的按鈕,或者增長x軸來獲取加號的按鈕。

CGPoint minusButton = stepperCenter;
minusButton.x -= CGRectGetWidth(repsStepper.frame) / 4;

CGPoint plusButton = stepperCenter;
plusButton.x += CGRectGetWidth(repsStepper.frame) / 4;

若是增長或者減小UIStepper寬度的1/4,就會正好到達加號、減號按鈕的中間位置。

瞧!這兩個點,至關於UIStepper控制器中的加號/減號按鈕的中心點。

觸擊屏幕的指定點是萬不得已的方法,但有時它是測試UI的惟一的方法。例如,你也許會有一個自行定製的控制器沒有實現UIAccessibility Protocol

定時器收官

好的,你已經到了定時器測試的最後一步了。你開始意識到用KIF作UI測試的潛力了麼?很好!

最後的步驟是設置重複次數,而後啓動定時器。添加如下代碼到UITests.m中的test20StartTimerAndWaitForFinish的結尾:

// 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]

這裏介紹在最後階段中即將會發生什麼:

  1. 上面的代碼尋找UIStepper的加號和減號按鈕的座標。
  2. waitForTimeInterval:調用增長了延時,這樣你能夠看到步數值的變化——不然的話,對於肉眼而言,它的變化太快了。
  3. 這個步數的最大值是20,因此觸擊減號按鈕20次會把值恢復到1。而後觸擊加號按鈕2次(以最小的延時交替)把重複數字變爲指望的值 3。
  4. 每一個測試步驟默認的超時時間是10秒。即便在加速調試模式下,有可能20多分鐘長度的「工做」也須要超過10秒鐘時間完成,因此須要設置一個比較寬鬆的超時時間——60秒。
  5. 觸擊「開始工做」按鈕,它會彈出模式視圖控制器。當重複都結束後,模式視圖控制器會消失。這意味着你將返回定時器視圖控制器上,因此 等待「開始工做」按鈕再次出現實際上是在等待模式視圖控制器消失。
  6. 重設超時時間爲10秒。

一旦你保存文件,你會發現有方法聲明一側會出現一個小的棱形圖標:

這是一個運行單個測試的按鈕。所以,你能夠在測試環境下只運行這個方法,而不須要運行整套測試用例。優雅!點擊菱形按鈕來運行這個測試,你會看到模擬器啓動,並運行測試用例。只要坐在一旁觀看它輸入任務名,滑動滾動條,以及定時器的時間流動就能夠了。不用動一根手指,你就能測試UI了。

成功了!如今你能夠成功設置測試,幫助你完成一系列UI控制器的操做。

若是你經過菱形按鈕跑單個測試,它只跑一個簡單的方法而不會在開頭調用beforeAll。若是你的測試是依賴於beforeAll的,你仍然須要跑整套完整的測試。

提早結束

「去工做!」模式視圖控制器中有一個放棄按鈕可讓用戶取消一個時間週期。你仍能夠測量已經工做的分鐘,即便很早就結束了測試。這個數據仍然會被記錄到歷史上,但會被標記爲整個週期沒有跑完。

你不用相信個人話,能夠本身嘗試一下。只需作一些相似上述測試的東西。設置定時器的參數,觸擊*開始工做按鈕,而後觸擊放棄按鈕。

不要立刻觸擊「放棄」按鈕——定時器須要運做一段時間,應用程序纔開始建立歷史記錄。因此,你能夠將鼠標移動到測試上,手動中止測試。或者,你能夠編程設定它在你選定的時間和位置。若是你生性猶豫,你能夠跳過這個挑戰。然而,若是你喜歡編寫應用去幫你處理各類瑣事,就試一下吧!你知道如何在「開始工做」和「放棄工做」之間增長一點延時麼?

你能夠調用waitForTimeInterval:在你的測試裏增長延時。

UITests.m中添加下列的測試方法,加在其餘測試的下面:

- (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"];
}

在確保你位於正確的選項卡以後,設置任務名而後選擇好預設。而後啓動定時器,在放棄以前等待3秒。

方法的最後一行等待模式視圖控制器消失,返回主界面。記住,默認的超時時間是10秒,可是實際上不須要這麼長時間——觸擊「放棄」按鈕,模式視圖控制器會當即消失。

在以前的測試中,你使用了類方法setDefaultTimeout:爲全部測試操做設置全局的超時時間。在這裏,你能夠調用usingTimeout:爲某一步定製的超時時間。

保存文件並點擊菱形按鈕運行單個測試。當定時器開始的時候,你能夠發現它在放棄、返回主界面以前會等待3秒。

歷史和拖動

以前咱們沒有怎麼關注歷史選項卡,如今該輪到它出一下風頭了。若是你按照練習一步一步來進行操做的話,那麼你至少會有一條歷史記錄。構建並運行該應用,而後切換到歷史選項卡。

歷史表視圖實現了最新的iOS7刪除手勢——當你把整行往左邊拖動,而後觸擊出現的「刪除」按鈕。這是你的下一個測試!這要求歷史中至少有一條記錄,因此你跑單個測試的時候要當心。你必須保證歷史記錄中有內容,不然就沒什麼能夠測的!安全起見,你須要完整地運行整套測試,由於前面的測試會建立一些歷史記錄供你使用。記住,測試的運行順序是根據字母順序排列的,因此你能夠很放心,前面的測試會建立一些歷史記錄的。

若是你看一下HistoryViewController.m中的tableView:cellForRowAtIndexPath:,你會發現每一格都能獲得一個輔助標籤,好比「第3行第0個」之類的。這有助於KIF找到這一行。但在真實環境下這種標籤很糟,因此不要在真正的應用中使用。在示例工程中,咱們使用#ifdef來確保這些只出如今調試構建中。你應該在你本身的應用中作相似的事情。請確保在發佈構建的時候,將輔助標籤設爲一些有用的內容,切實爲用戶提供輔助功能。

如今,打開UITests.m並添加以下的測試方法:

- (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"];
}

如今介紹上面方法的做用:

  1. 切換到「歷史」選項卡。
  2. 引用表視圖並追蹤總共存在多少行。若是一行數據有沒有,測試就會失敗。
  3. 往左拖動表視圖的單元格。當刪除按鈕出現後,觸擊它。
  4. 刪除一個單元格的時候,表視圖會有一個動畫效果,因此在進行下一步前添加一個短暫的延時。再次檢查表視圖的行數,應當比以前的行數少一行。

用Command-U運行整套測試,全部的測試會順序執行。

如今,你的測試覆蓋了整個應用的基本流程了——從數據重置到屢次運行定時器,再到驗證和刪除歷史記錄。

若是你要繼續開發這個應用程序,這些測試會是一個很好的基礎,確保界面沒有退步。這意味着在主界面變化的時候你須要更新你的測試——測試就像是你應用的規格說明,須要及時更新才能發揮做用。

進一步

到目前爲止,你應該對KIF的可能性有很好的瞭解,腦子裏也應該有很多主意,如何利用這個高效的功能測試工具來測試你本身的應用程序。添加KIF到你本身的項目的時候,你能夠查看這個文檔。你能夠手動添加KIF或者使用很是方便的CocoaPods依賴管理。

KIF是一個開源項目,且有許多新功能還在不斷開發之中。例如,在寫這篇教程的時候,下一個KIF的發佈版將會包含截屏的功能。這意味着能夠在跑測試的時候經過屏幕截圖來查看全過程當中的關鍵點。聽起來這不是比用肉眼觀察KIF點擊和拖動整個過程好上千萬倍麼?KIF正變得愈來愈好了,因此學習如何使用它,是一個很好的自我投資。

最後,因爲KIF測試用例是OCUnit的子類,並在標準的Xcode5測試框架下運行,你可使用持續集成來跑這些測試。當你幹別的事情的時候,你擁有了一個可以像人的手指同樣觸控的機器人去測試你的應用程序。太棒了!

你能夠在這裏下載整個示例項目的代碼。測試快樂!


原文 iOS UI Testing with KIF
翻譯 SegmentFault

相關文章
相關標籤/搜索