標籤:des http os 使用 io strong for 檔案 ar
轉載自http://www.infoq.com/cn/articles/ios-unit-test-1
導讀:本文不討論單元測試是什麼,或者它之於一個工程的利弊,我認為單元測試是一個開發人員保證產出代碼品質的有效工具。本文從使用者的角度對比當下比較流行的兩款單元測試架構,給大家提供一些選用建議。如果你還不甚瞭解單元測試在工程中所起到的作用,或者還不知道TDD的開發模式,可參考:Test-Driven Development和Unit Testing。
本文對比兩個iOS開發中常見的單元測試架構:OCUnit,被官方整合進XCode 4.x版本中;GHUnit,被推薦最多的測試架構,帶GUI介面。初窺兩款測試架構非常相似,而上手使用就會發現其中的區別。細節上的區別使兩款架構在不同角度各有優劣。
OCUnit
相關廠商內容
基於Docker的DevOps ——QCon上海2014主題演講豌豆莢架構師周愛民加入ArchSummit北京2014大會組委會AWS技術演講:AWS設計模式在Autodesk的實現如何建置工程師文化團隊白皮書下載:發行管理的痛點與最佳實務
OCUnit是XCode 4.x整合的單元測試架構,OCUnit中的測試分為兩類,一類稱為Logic Tests,另一類稱為Application Tests。Logic Tests更傾向於所謂的白盒測試,用於測試工程中較細節的邏輯;Application Tests更傾向於黑箱測試,或介面測試,用於測試直接與使用者互動的介面。
• 添加單元測試
OCUnit是XCode整合的,所以其與工程的結合理應是最好的,添加到工程中的成本也理應最低。使用XCode建立新工程的流程中就有一個“Include Unit Tests”的選項(1),新的工程就會自動產生一個Logic Tests。
向已存在的工程中添加OCUnit Logic Tests也不複雜,只需要添加一個類型為:“Cocoa Touch Unit Testing Bundle”的Target即可(2)。
圖2,向已存在的工程中添加OCUnit測試
向已有工程中添加一個測試Target時,XCode會自動產生一個Scheme,運行單元測試用例和Build原工程需要切換不同的Scheme。如果認為切換Scheme非常麻煩,也可以在添加Target之前,在“Manage Scheme”菜單中取消“Autocreate schemes”(3)。
圖3,添加Target不建立Scheme
Application Tests要基於Logic Tests做一些修改。一般來說一個工程既需要Logic Tests也需要Application Tests,所以建議按照上述方法添加一個單獨的Target,然後執行以下操作(4):
1. 在Build Settings中搜尋“bundle loader”,設定為:$(BUILT_PRODUCTS_DIR)/APP_NAME.app/APP_NAME(APP_NAME是應用程式名稱)
2. 再搜尋“test host”,設定為:$(BUNDLE_LOADER)
3. 在Build Phases-Target Dependencies中添加依賴,選擇主程式Target
圖4,添加一個Application Tests
• 建立測試案例
OCUnit的測試案例最常用的方法有三個
1. - (void)setUp:每個test方法執行前調用
2. - (void)tearDown:每個test方法執行後調用
3. - (void)testXXX:命名為XXX的測試方法
添加Target之時XCode已經自動建立了一個測試案例類:UnitTestDemoTests,其中UnitTestDemo是工程的名字,該類中已經包含了setUp,tearDown和testExample三個方法。
通過command+n,選擇“Objective-C test case class”建立一個新的測試案例類(5)。通過XCode建立的測試案例類是一個繼承自SenTestCase(OCUnit由SEN:TE公司開發,因此基類命名為SenTestCase)的空類,需要模仿UnitTestDemoTests編寫測試方法。
圖5,建立一個測試案例類
開發人員可以自己實現無傳回值,且命名規則為testXXX的執行個體方法,並使用架構提供的大量斷言方法。
Logic Tests與Application Tests的區別主要在setUp方法,Logic Tests只需在setUp方法中初始化一些測試資料,而Application Tests需要在setUp方法中擷取主應用的AppDelegate,供test方法調用。
值得注意的是,OCUnit的test bundle是侵入主應用的,因此在使用過程中要十分注意,不要讓單元測試的資源覆蓋主應用資源,造成詭異的Bug。
• 運行測試
由於OCUnit是整合在XCode中的架構,因此在XCode中運行也比較方便。切換到單元測試的scheme(如果與工程共用scheme則無需切換),Product->Test(或直接使用快速鍵command+u),架構會自動尋找所有工程中SenTestCase的子類,運行其中全部命名類似testXXX的無傳回值方法。
• 測試反饋
OCUnit的失敗方法會通過Console和XCode Issues兩個位置反饋,通過XCode Issues可以直接定位到出現錯誤的單元測試程式碼。Issue的提示資訊就是在單元測試斷言方法中定義的description。
GHUnit
GHUnit是一款Objective-C的測試架構,除了支援iOS工程還支援OSX的工程,但OSX不在本文的討論範圍。GHUnit不同於OCUnit,它提供了GUI介面來操作測試案例,而且也不區分Logic Tests和Application Tests。
• 添加單元測試
與整合進XCode的OCUnit相比,GHUnit的添加過程略顯複雜。首先在上下載GHUnit的架構套件,當前的For iOS的最新版本是0.5.6,解壓後是一個GHUnitIOS.framework的檔案夾。
開啟已經存在的工程,添加一個EmptyApplication Target,並在新Target中添加剛剛下載的GHUnitIOS.framework(6、7)。
圖6,在新Target中添加GHUnitIOS.framework
在Build Phases中添加非官方架構並不會把架構檔案拷貝到工程目錄,而是只做一個連結,所以建議在添加之前先把架構拷貝到工程目錄下。
圖7,選擇GHUnitIOS.framework
接下來用相同的方法添加架構依賴的其他庫:“QuartzCore.framework”。
在Build Settings中搜尋“linker flags”,設定Other Linker Flags - Debug - 添加一個支援全架構和全版本SDK的標示“-ObjC -all_load”(8)。
圖8,設定linker flags
刪除Tests Target中的AppDelegate(.h和.m一起刪除)。修改main函數,支援GHUnitIOS,匯入GHUnitIOSAppDelegate代替原來的AppDelegate,修改UIApplicationMain的參數(9)。
圖9,修改main函數
至此已經完成了GHUnit的添加,選擇建立Target同時建立的scheme,直接Build and Run即可在裝置或Simulator中啟動一個新的App(10),即該單元測試的App。
圖10,單元測試App
• 建立測試案例
建立GHUnit測試案例與建立OCUnit測試案例相似。
建立一個Objective-C Class檔案,繼承自GHTestCase,在XCode產生的.h檔案中不會匯入GHUnit.h檔案,需要開發人員自行匯入“#import <GHUnitIOS/GHUnit.h>”。
GHUnit架構提供斷言方法比OCUnit更加豐富,開發用例也就可以做的更加細緻,更有利尋找/定位錯誤。
測試方法的命名規則與OCUnit一樣,是以test開頭的無傳回值方法:- (void)testXXX。而常用的方法除了上述提到的setUp和tearDown,GHUnit還提供了setUpClass和tearDownClass兩個方法,在該用例運行前和結束後調用。另外,剛剛提到GHUnit不區分Logic Tests和Application Tests,所以在setUp和tearDown方法中也就不存在設定的區分。
• 運行測試
運行GHUnit需要分兩步,首先編譯並安裝單元測試App到裝置或Simulator裡(11),建立了兩個用例,每個用例中分別有一個方法。
圖11,兩個用例的GHUnit App
在App中可以通過點擊右上方的Run按鈕運行全部用例,架構會尋找所有以testXXX命名的無傳回值方法,並執行。或點擊TableView中的某個Cell運行單獨的測試方法。
• 測試反饋
宣告失敗測試未通過的方法在App中會標記為紅色,並給出每一個方法的已耗用時間。在Console中會列印出詳細的出錯資訊,包括:異常類型,出錯檔案,位置,以及斷言方法中指定的出錯原因。更重要的是,出錯時的程式堆棧內容(12)。
圖12,未通過測試的方法,Console中的內容
GHUnit通過Console中的內容給開發人員提供協助,可以快速定位程式出錯的位置,這一點比OCUnit做的要好。
總結
GHUnit在安裝上確實顯得有些麻煩,無法跟整合在XCode裡的OCUnit相比。 但從開發人員的角度講,我更喜歡GHUnit帶來的體驗,GUI的操作介面可以脫離IDE單獨運行,支援運行單一測試方法和運行全部用例的,列印出錯堆棧可以更快定位到問題所在。
本文簡單介紹了兩款架構的安裝與入門,可以初步瞭解其各自特點,在接下來的文章中將會更加詳細的介紹如何使用架構進行單元測試,以及架構中的一些進階功能。此外,後續還將向大家介紹另外的與這兩款架構區別更加明顯的單元測試架構。
iOS中的單元測試(一)