做者:Maxime Defauw,原文連接,原文日期:2016-02-29
譯者:ray;校對:numbbbbb;定稿:waygit
每一個 iOS 程序員都要時不時的爲他們的 app 作 debug。除非你是那種超級大牛,不然你確定體驗過查了無數個小時的 bug 最後才發現那僅僅是個簡單的語法錯誤時那種油然而生的絕望感。或者更糟:你根本就沒發現那些 bug。不管你是編程新手,仍是開發過不少 app 的老司機,例行的寫寫單元測試會讓你的代碼更可靠,更安全,更容易 debug!程序員
你很走運,Xcode 7 和 Swift 支持單元測試。儘管單元測試不保證(有了它你就會寫出)絕對沒有 bug 的 app,它仍是一種能讓你驗證每段代碼是否如期工做,並讓 debug 過程更加便利。github
正如其名,在單元測試中你要爲某段代碼單元建立一些小規模的、針對其某個特性的測試,而後確保每一個代碼單元都能經過這些測試。若是經過的話,它的旁邊會出現一個綠色小標誌,而若是因故測試不經過, Xcode 會把該測試標記爲 "failed"。這就提示你去查看代碼,找出失敗緣由。express
首先下載這個我爲你準備的 starting project。一個短小精悍的 app:它會對一個給定的數字和百分比作一個乘法計算。(好比80的10%是8。)編程
這個 PercentageCalculator 項目很是簡單。你惟一須要關注的就是 ViewController.swift 這個文件。裏面的代碼都標記了註釋,很容易理解。swift
有 5 個 IBOutlets:每個都對應了屏幕上一個 UIElement,除 title(標題)以外,還有 2 個 slider 對應 2 個 IBActions。每一個 IBAction 的方法名都精確描述了其用途及將要執行的操做。當一個 slider 值改變時,其對應着的百分比或數字的值也會隨之改變。安全
還有兩個簡單的函數 「updateLabels()」 和 「percentage()」 作了符合期待的事情:當一個 slider 改變時第一個函數更新 label,第二個函數獲取兩個浮點數並返回百分比的計算結果。app
在模擬器中運行 app。剛開始一切看起來都很正常。但當你開始改變數字時就會發現計算結果有問題。爲找到 bug,咱們將代碼分割成不一樣的單元,而後分別作測試,看看每一個是否都如期運行。這不會解決 bug,但能縮小你的查找範圍。框架
我建立項目的時候,默認狀況下會勾選建立一個 test 文件的選項(若是你想要手動加一個的話,在 iOS Source 下面選擇 select File > New > File > Unit Test Case Class)。咱們的例子中 test 文件已經被 Xcode 自動建立出來,能夠在項目導航欄中 「PercentageCalculatorTests」 文件夾中找到它。異步
在 PercentageCalculatorTests.swift
文件中,PercentageCalculatorTests
類裏面已經爲咱們建立好了 4 個方法。其中 2 個是測試方法(test methods)的例子,你能夠刪掉它們(它倆都以 test
關鍵字開頭,而且它們左邊的豎條中都有個方塊形圖標,名字也都以 「...Example」 結尾,因此你能夠經過這些辨識出來它們是測試方法)。另外兩個方法,setUp()
和 tearDown()
是特殊的樣板方法(boilerplate methods),它們分別在每一個測試方法被執行以前,和每一個測試方法被執行以後被執行。
如今是時候寫你的第一個單元測試函數了!本教程咱們只測試 ViewController
類,須要在 PercentageCalculatorTests
中添加一個它的實例。
class PercentageCalculatorTests: XCTestCase { var vc: ViewController! override func setUp() { super.setUp() // 這裏寫setup的代碼。本class裏每一個測試函數被調用以前該方法都會被先調用。 } override func tearDown() { // 這裏寫teardown的代碼。本class裏每一個測試函數被調用以後該方法都會被調用。 super.tearDown() } }
PercentageCalculatorTests
是一個 XCTestCase
的子類,後者被打包在 XCTest 框架中。每個 XCTestCase
子類的實例都負責對你項目的某個特定部分作測試,好比對一個特性作測試。
在 setup 方法中實例化一個 vc
。這樣對每個測試方法你都會獲得一個「全新的」 ViewController
實例,由於在每一個測試方法執行前 setUp()
都會被調用一次。把 setUp()
方法修改以下:
override func setUp() { super.setUp() let storyboard = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle()) vc = storyboard.instantiateInitialViewController() as! ViewController }
如今你應該記得全部的測試方法的名字都要以 test
關鍵字開頭,不然 Xcode 不會識別。添加一個新的 testPercentageCalculator()
測試方法,來驗證一下 ViewController
中的 percentage()
工做是否正常。
func testPercentageCalculator() { }
單元測試中你要去檢查某段代碼是否如你所願的那樣工做。待測試的代碼段通常都只有幾行,典型狀況是你只須要測試一個方法或者一個函數。單元測試是這樣去作的:你給某個代碼單元一個輸入值,讓這個值過一遍這段代碼,而後檢查一下輸出的值是否和預期的同樣。
與「咱們指望的那個值」作比較的這部分由 XCTAssert
函數來處理。最簡單的 XCTAssert
函數是XCTAssert(expression: BooleanType)
。這個函數要求一個布爾表達式(相似於 5>3
,8.90 == 8.90
或者 true
這種),隨後若是表達式爲真則讓測試經過,不然認爲測試失敗。
嘗試一下!首先給 testPercentageCalculator()
方法加添加下面一行。而後把光標移到方法名左邊側欄的那個方塊圖標上,停下光標以後方塊圖標變成了一個執行光標,點擊一下就開始了測試。
func testPercentageCalculator() { XCTAssert(true) }
若是一切順利,則測試經過,方法左邊會出現一個綠色檢測標。
如今來真的:測試 percentage()
方法!用 ViewController
的一個實例 - vc
屬性來調用這個方法。給這個方法兩個浮點數,好比 50 和 50,而後把結果存儲到常量 p
中。這個例子中 p
應該是 25(50 的 50% 是 25)。而後用 XCTAssert(p == 25)
檢測一下是否是這樣,執行測試方法。把 testPercentageCalculator()
改爲這樣:
func testPercentageCalculator() { // 應該是25 let p = vc.percentage(50, 50) XCTAssert(p == 25) }
測試成功了,這意味着 ViewController
的 percentage()
函數工做正常,咱們應該在其餘的地方繼續尋找 bug。也許 bug 在 updateLabels()
裏面?
如今添加一個新的測試方法 testLabelValuesShowedProperly()
來驗證一下 label 能不能正確的顯示 text。和以前同樣,調用 ViewController 的一個方法 - 這回是 updateLabels()
- 而後看看每一個標籤的 text
屬性和咱們指望的那個 text 是否相同。
注意到你要給 XCTAssert
函數傳一個新的參數:一個 string 類型的消息。這對咱們此次要對多個值作檢查(調用三次 XCTAssert )來完成測試而言就會很方便。若是測試失敗,這條消息就會指名咱們具體是哪裏錯了。
func testLabelValuesShowedProperly() { vc.updateLabels(Float(80.0), Float(50.0), Float(40.0)) // labels應該顯示80, 50 and 40 XCTAssert(vc.numberLabel.text == "80.0", "numberLabel doesn't show the right text") XCTAssert(vc.percentageLabel.text == "50.0%", "percentageLabel doesn't show the right text") XCTAssert(vc.resultLabel.text == "40.0", "resultLabel doesn't show the right text") }
你嘗試執行這個測試方法時,會收到編譯器的錯誤提示:numberLabel
,percentageLabel
和 resultsLabel
是 nil
。怎麼回事呢?
我是在 storyboard 文件中建立了這些 labels 的,所以只有當 view 被加載以後(loaded)它們纔會被初始化,然而因爲對單元測試來講 loadView()
方法不會被觸發,因此這些 labels 沒有被建立,只能是 nil
。一種可能的方法是經過調用 vc.loadView()
來解決,可是 Apple 在它的文檔中並不推薦你這麼作,由於當已經被加載的對象又被加載一次的話可能會引發內存泄露。
正確的方法是你應該先訪問一下 vc
的 view
這個屬性,這會讓 vc
反過來觸發全部相應的方法,不只僅包括 loadView()
。把 testLabelValuesShowedProperly()
改爲這樣:
func testLabelValuesShowedProperly() { let _ = vc.view vc.updateLabels(Float(80.0), Float(50.0), Float(40.0)) // labels應該顯示80, 50 and 40 XCTAssert(vc.numberLabel.text == "80.0", "numberLabel doesn't show the right text") XCTAssert(vc.percentageLabel.text == "50.0%", "percentageLabel doesn't show the right text") XCTAssert(vc.resultLabel.text == "40.0", "resultLabel doesn't show the right text") }
注意到下劃線(_)忽略了常量的名字。由於咱們實際上並不須要用到這個 view。加下劃線就是告訴編譯器「你僞裝訪問一下這個 view,把相應的方法觸發就行。」
執行測試。(若是想一併執行咱們test類的全部測試,你還能夠點擊 「class PercentageCalculatorTests」 旁邊的那個方塊)。
如你所見,測試失敗了!咱們給 XCTAssert 方法傳入的錯誤細節消息幫助咱們快速識別出引發 bug 的可能緣由。此次測試告訴咱們 resultsLabel 沒有顯示出正確的文本,因此咱們進到 ViewController 裏看看對這些 label 的 text 值是在那裏被設置的。仔細看了 ViewController.swift
的 updateLabels()
代碼以後,咱們發現了 bug 的緣由:
self.resultLabel.text = "\(rV + 10)"
應該是:
self.resultLabel.text = "\(rV)"
更新代碼以後再運行一次測試,一切都應該正常了!
本篇教程中你學到了 Xcode 中的單元測試的相關內容,以及它怎樣可以幫你找到代碼中的 bug。除了預防 bug 以外,單元測試還能夠用來作性能測試和異步測試。還可能讓你感興趣的是UI測試,你能夠錄製下你在 app 上作出的動做來測試你的 app 在實際使用情景下是如何表現的。若是聽起來以爲感興趣,那必定要看看這個講 UI 測試的 WWDC視頻。
項目的最終版本能夠在 Github上下載。
若是你有關於 UI 測試的任何問題,或者學習本教程中遇到了困難,請在評論中點我!
做者介紹:Maxime Defauw 是一個有經驗的程序員,在 App Store 和 Google Play store 上發佈過多個 app。他今年 16 歲,居住在比利時。最近他在 San Francisco 舉行的 WWDC15 上得到了 Apple 的獎學金。Max 熟練掌握 Objective-C,C,C#,如今是 Swift。不碼代碼的時候他通常在曲棍球場或者高爾夫球場上。在 Twitter 上 @MaximeDefauw 粉他。
本文由 SwiftGG 翻譯組翻譯,已經得到做者翻譯受權,最新文章請訪問 http://swift.gg。