測試這個詞很容易理解,那麼什麼是單元(Unit)呢?
一個單元指的就是應用程序中可以測試的最小單元。
一組源代碼可以測試,一般要求有明確的輸入與輸出。因此一般來說源代碼中明確的包含輸入輸出的每一個方法被認為一個測試的單元(一個case)。
注意,這裡的輸出並不局限於方法的返回值對輸入參數的改變,也包括方法在執行過程中改變的任何數據。
單元測試在程序裡面可以理解一個模塊一個方法,在每個可能存在的模塊都進行測試,確保每個模塊都沒有問題,從而提高整體程序的質量。
單元測試的目的是將程序中所有的源代碼,隔離成最小的可測試單元,以確保每個單元的正確性,如果每個單元都能保證正確,就能保證應用程序整體相當程度的正確性。另一方面測試腳本本身就是被測試代碼的實際使用代碼,這對於開發者理解被測試單元的使用是用幫助的。
測試是分黑盒測試和白盒測試(概念此處不在解釋),單元測試其實就是一種白盒測試,開發者對現有已經實現的模塊自己寫對應測試腳本進行測試,這中間還包含測試用例的設計。相對來說還是由開發者自己來完成白盒測試,然後在交由測試團隊進行黑盒測試,這樣也更加有助於提升測試流程的完整性,最終提高產品的質量。
單元測試的內容:
單元測試的測試目的 模塊接口測試 局部數據結構測試 路徑測試 錯誤處理測試 邊界測試在現有的開發工作中,我們一般都會忽略掉單元測試的重要性,功能開發完成以後開發者拿到現有的測試用例,直接針對每條用例進行手工的測試,測試通過就進行提測,之後測試人員還是重復手工測試的流程、數據的mock、專項測試等,這樣以來白盒測試的流程有時間份量會變的很低,開發人員不知道自己模塊代碼的覆蓋路問題,更多的時間可能某些代碼一直到到上線都從來沒有跑過,以至於到了真實環境下會產生一些意想不到的問題,這樣以來風險極高,整體來說單元測試還是至關重要的。
下面介紹一下Xcode7 中現有的一些測試工具:
本文主要基本Xcode7來講解。
最新的Xcode7中是包含了UITest UnitTest工具的,這個可以在你創建工程的時間勾選對應的選項,也可以直接通過addTarget的形式來完成,
如果項目創建的時間勾選了UnitTest(從名字上看就是Apple提供的官方的一個單元測試工具) ,我們可以看到工程裡面是多了一個目錄,默認多了一個類, 如圖:
選擇XCTest:
XCTest時Apple官方提供一個測試工具,一個內置的測試框架,從工程裡面可以看到,一個“應用名稱”的group,我們直接可以使用commond+R 來遠行,一個測試的target我們可以使用commond+U來遠行測試target,在測試target的目錄下會有一個默認的“應用名稱”+Test的類,這個類只有.m沒有.h,繼承於XCTestCase,使用commond+U即可運行。
默認測試類裡面有以下方法:
//方法在XCTestCase的測試方法調用之前調用,可以在測試之前創建在test case方法中需要用到的一些對象等
- (void)setUp ;
//當測試全部結束之後調用tearDown方法,法則在全部的test case執行結束之後清理測試現場,釋放資源刪除不用的對象等
- (void)tearDown ;
//測試代碼執行性能
- (void)testPerformanceExample
XCTestCase的初始化不是用戶控制的,開發者無需手動針對XCTestCase的subclass進行 alloc 、init或者調用靜態方法初始化的操作,針對一個功能塊的單元測試(針對某個class),只需要單獨給為這個類創建一個繼承於XCTestCase,在這個文件內實現上述基本函數以後(一半系統會默認創建這三個函數),其實的邏輯只需要開發者自行定義以“test”開頭的函數,然後在內部實現自己針對某個函數、返回數值結果、操作等的測試腳本即可,commond+U執行的時間,單元測試會自動執行這些test打頭的函數,當函數頭上出現藍色的標記則表明測試通過,否則直接報紅色錯誤。
XCTest測試范疇:
基本邏輯測試處理測試 異步加載數據測試 數據mock測試XCTest常用的一些判斷工具都是以XCT開頭的,如:
//斷言,最基本的測試,如果expression為true則通過,否則打印後面格式化字符串
XCTAssert(expression, format...)
//Bool測試:
XCTAssertTrue(expression, format...)
XCTAssertFalse(expression, format...)
//相等測試
XCTAssertEqual(expression1, expression2, format...)
XCTAssertNotEqual(expression1, expression2, format...)
//double float 對比數據測試使用
XCTAssertEqualWithAccuracy(expression1, expression2, accuracy, format...)
XCTAssertNotEqualWithAccuracy(expression1, expression2, accuracy, format...)
//Nil測試,XCTAssert[Not]Nil斷言判斷給定的表達式值是否為nil
XCTAssertNil(expression, format...)
XCTAssertNotNil(expression, format...)
//失敗斷言
XCTFail(format...)
Xcode單元測試中加入的最令人興奮的功能也許就是類XCTestExpression類帶入的異步測試了。現在測試可以等待指定長度的時間,一直到某些條件符合的時候在開始測試。而不用再寫很多的GCD代碼控制。
要使用異步測試,首先用方法expectationWithDescription創建一個expection
let expectation = expectationWithDescription("...")
之後,在方法的最後添加方法waitForExpectationsWithTimeout,指定等待超時的時間和指定時間內條件無法滿足時執行的closure。
waitForExpectationsWithTimeout(10) { (error) in
// ...
}
剩下的就是在異步測試剩下的回調函數中告訴expectation條件已經滿足。
expectation.fulfill()
如果在測試中有多個expectation,則每個expectation都必須fulfill,否則測試不通過。
- (void)testFetchRequestWithMockedManagedObjectContext
{
MockNSManagedObjectContext *mockContext = [[MockNSManagedObjectContext alloc] initWithConcurrencyType:0x00];
let mockContext = MockNSManagedObjectContext()
NSFetchRequest * fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"User"];
let fetchRequest = NSFetchRequest(entityName: "User")
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"email ENDSWITH[cd] apple.com"];
fetchRequest.predicate = NSPredicate(format: "email ENDSWITH[cd] %@", "apple.com")
fetchRequest.resultType = NSDictionaryResultType;
fetchRequest.resultType = NSFetchRequestResultType.DictionaryResultType
var error: NSError?
NSError *error = nil;
NSArray *results = [mockContext executeFetchRequest:fetchRequest error:&error];
let results = mockContext.executeFetchRequest(fetchRequest, error: &error)
XCTAssertNil(error, @"error應該為nil");
XCTAssertEqual(results.count, 2, @"fetch request應該只返回一個結構");
NSDictionary * result = results[0];
XCTAssertEqual(result[@"name"], @"張三", @"name應該是張三");
NSLog(@"email : %@",result[@"email"]);
XCTAssertEqual(result[@"email"], @"[email protected]", @"email應該是[email protected]");
}
數據mock
代碼覆蓋率,故名思義 代碼覆蓋率 = 實際執行的代碼行數 / 整個工程總代碼行數,直白來講就是這樣一個數值,上述談過,單元測試的目的除了講程序分成各個最小的單元獨立去測試確保正確以外,還有一個就是代碼覆蓋率問題,如果說發到線上的產品有相當一部分代碼從來都沒有執行過,這個問題是相當危險的(問題大家可以各自猜測,相信這個問題不是很陌生)。
Xcode7 提供了一個內置的Code Coverage工具組件,廢話不說,下面看使用方法:
1、首先需要在product->scheme->Edit Scheme裡面將Code Coverage模式打開,選中為debug模式,如圖:
Edit Scheme:
2、打開Code Coverage模式之後,打開某個測試類,commond+U 運行,如果測試通過,測試腳本的函數頭上會出現一個綠色的標志(相反如何哪一個方法測試沒有通過,則會提示一個紅色錯誤),如下:
3、打開Xcode左邊窗口的Report Navigator,找到 Project Log,選擇最近一次的log選項,最近一次是剛才的一個Test Log,選擇中這個Log實例,可以看到一下界面,
如圖:
然後在tab中選中 Coverage,此時你可以看到大致的代碼執行覆蓋情況,如果指示條是滿的則代表該類代碼全部跑過一遍。
4、雙擊你想要查看的類,此處選擇查看UATrackDao,打開後既可以看到剛剛的測試中有哪些代碼是執行過的,那些代碼時未執行的,橘黃色的代表還未執行的,執行過的每一行後面會有一個序號代表這行代碼在剛才的測試過程中執行的次數。如果有未執行的,可根據具體的情況調整對應的測試腳本,繼續測試,最終確保每一行代碼都能正確執行,如圖:
任何軟件開發中,自動化UI測試都是很重要的(UI自動化測試的好處此處就不再多說了),iOS平台在以往是通過UIAutomation來完成自動化UI測試的,它的測試用例是javascript寫的(Instruments中提供了該功能),這個過程深奧繁瑣,需要自行編寫對應的測試腳本,速度慢,學習成本高(關於Automation自動化測試概念大家可以查看相關的資料,Automation自動化測試在各大平台都有應用,在大型的軟件開發測試過程的確的確可以節省大量的手工測試人員,大大提高軟件測試的成本與效率,在最新Xcode7本文推薦使用Apple提供的最新的工具UITest)。
Apple在Xcode 6中又新增了UnitTest之外,到了Xcode 7 Apple從新提供了一個新的框架UITest,這個主要是用來測試UI的,UnitTest湧來測試功能邏輯代碼,UITest專門用來測試UI。
Xcode 7已經集成了UITest,UITest允許你找到UI元素並與之交互,還能檢查UI的屬性和狀態,並且UITest也已經集成在Xcode 的測試報告了,可以和單元測試一起執行,和UnitTest一樣我們可以在檢查UI的時間執行斷言。
創建UITest target,同樣會生成一個“項目名稱”+UITest的group,UITest target可以在創建工程的時間勾選,也可以在工程中手動添加,在 “項目名稱”+UITest 分組下,我們可以看到系統會幫我們默認生成一個UI測試類,這個類也同樣是繼承於XCTestCase的。由此可見,在iOS中無論是單元測試還是UI測試,都是基於XCTestCase的。
創建模態視圖,我們選擇從第一個VC通過點擊按鈕的形式push到第二個VC
創建UITest target,我們對上述UI進行測試 如圖選項:
打開UATrackDemoUiTest.m
創建 - (void)testUI,同時將光標留在函數內
點擊下面的紅色按鈕,開始recorder操作,程序運行起來後,點擊界面上的按鈕,程序會push到一個新的頁面,這個時間會看到到剛才的鼠標光標處自動生成了一部分代碼,重復操作,每次都會生成新的代碼,如圖:
從新點擊小紅點按鈕,此時結束recorder操作,commond+U 運行測試,此時剛才的一連串動作會一步一步連續執行下來
此處聲明: 第一次點擊紅色的recorder按鈕,然後手動操作會自動生成測試腳本,第二次commond+U是進行測試UI.
在我們開始錄制動作之前,必須要決定需要斷言什麼內容。我們可以使用XCTest框架來對UI中的某些內容進行斷言,現在框架中已經包含下面三個新API。
XCUIApplication。這是你正在測試的應用的代理。它能讓你啟動應用,這樣你就能執行測試了。它每次都會新起一個進程,這會多花一些時間,但是能保證測試應用時的狀態是干淨的,這樣你需要處理的變量就少了些。
XCUIElement。這是你正在測試的應用中UI元素的代理。每個元素都有類型和標識符,結合二者就能找到應用中的UI元素。所有的元素都會嵌套在代表你的應用的樹中。
XCUIElementQuery。 當你想要找到某個元素時,就會用到 element query。每個 XCUIElement 裡都包含一個query。這些query搜索 XCUIElement 樹, 必須要找到一個匹配的。否則當你視圖訪問該元素時,測試就會失敗。 例外是exists 屬性,你可以使用這個屬性來檢查一個元素是否展示在樹中。 這對於斷言很有用。 更一般地你可以使用 XCUIElementQuery 來找到對accessibility可見的元素。Query會返回結果的集合。
現在你已經可以使用CLI運行測試了,無需進一步的調整,不過我們想讓UI Tests在一個單獨的scheme中,點擊scheme並選中New Scheme:
選中最新創建的UI Test target並確認。
如果你計劃在CI-server上運行測試,請確保最新創建的scheme已經啟用了Shared選項。點擊scheme並選擇Manage Schemes來打開會話。
xcodebuild -workspace App.xcworkspace \
-scheme "SchemeName" \
-sdk iphonesimulator \
-destination 'platform=iOS Simulator,name=iPhone 6sp,OS=10.0'
test
明確所用Xcode的版本非常重要,我們使用的是最新的測試版本:
export DEVELOPER_DIR="/Applications/Xcode-beta.app"
你甚至可以將測試版作為你新的默認運行版本:
sudo xcode-select --switch "/Applications/Xcode-beta.app"
無需額外的工作,可輕輕松得到截圖。可為命令添加derivedDataPath選項,你可以告訴Xcode在什麼地方儲存測試結果,包括生成的截圖。
xcodebuild -workspace App.xcworkspace \
-scheme "SchemeName" \
-sdk iphonesimulator \
-destination 'platform=iOS Simulator,name=iPhone 6sp,OS=10.0'
-derivedDataPath './output'
test
XCTest一共提供了三種UI測試對象
XCUIApplication 當前測試應用target XCUIElementQuery 定位查詢當前UI中xctuielement的一個類 XCUIElement UI測試中任何一個item項都被抽象成一個XCUIElement類型當我們獲取了錄制生成的代碼以後,根據UITest提供的三種對象,我可以在此來對測試代碼進行修改,調試。
UITest中同樣適用以下斷言等:
XCTAssert(expression, format...)
//Bool測試:
XCTAssertTrue(expression, format...)
XCTAssertFalse(expression, format...)
//相等測試
XCTAssertEqual(expression1, expression2, format...)
XCTAssertNotEqual(expression1, expression2, format...)
//double float 對比數據測試使用
XCTAssertEqualWithAccuracy(expression1, expression2, accuracy, format...)
XCTAssertNotEqualWithAccuracy(expression1, expression2, accuracy, format...)
//Nil測試,XCTAssert[Not]Nil斷言判斷給定的表達式值是否為nil
XCTAssertNil(expression, format...)
XCTAssertNotNil(expression, format...)
//失敗斷言
XCTFail(format...)
.....
關於Xcode 7 UnitTest的問題就講到此處,希望有興趣的同學大家共同交流…
Xcode7的內置工具終於足夠的好了。也就是說即使是很大的APP也沒有必要為了單元測試的代碼覆蓋率而排斥Xcode內置的測試工具。無論什麼樣的測試,XCTest的各種斷言、expectation和性能測試都足夠應對。但是無論多好的工具,都需要用好才行。
如果你在測試iOS或者OS X的APP,開始為自動添加的測試類添加一些斷言並按下Command+U。你一定會發現感覺這些工具讓你的測試方便不少 。