- 測試導航(test navigator)
當進行測試時您访,你會經(jīng)常使用到Xcode的測試導航(test navigator)沿侈。
測試導航(test navigator)是workspace的一部分芽突,使創(chuàng)建纬乍、管理、運行和評估測試更加加單粒褒。通過點擊導航選擇欄中的圖標進入測試斤寂,該圖標在問題導航和調(diào)試導航之間耿焊。

注意:Xcode 測試目標(target)生成的測試包(test bundle)顯示在測試導航(test navigator)處。
- 測試目標


點擊測試文件右側(cè)
編譯運行測試用例編譯運行

綠色為測試通過遍搞、紅色為不通過

灰色菱形代表性能測試 這也就能說明這里為什么沒有通過或是失敗的斷言點擊此處會出現(xiàn)性能測試結(jié)果面板罗侯,此面板允許設置性能基準以及編輯基線和最大參數(shù)

關于測試文件方法
- 所有的測試用例方法保證以test開頭
-
setUp
在測試用例之前調(diào)用的初始化方法tearDown
釋放測試用例資源在每個測試用例完成后調(diào)用testExample
例子testPerformanceExample
測試性能的例子,在block內(nèi)寫測試性能的代碼溪猿,設置baseline(基準)和stddev(標準偏差)來判斷方法是否能通過性能測試钩杰。
- (void)setUp {
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
}
- (void)tearDown {
// Put teardown code here. This method is called after the invocation of each test method in the class.
[super tearDown];
}
- (void)testExample {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
}
- (void)testPerformanceExample {
// This is an example of a performance test case.
[self measureBlock:^{
// Put the code you want to measure the time of here.
}];
}
斷言
XCTest
XCTFail(format…) 生成一個失敗的測試纫塌;
XCTAssertNil(a1, format...)為空判斷,a1為空時通過讲弄,反之不通過措左;
XCTAssertNotNil(a1, format…)不為空判斷,a1不為空時通過避除,反之不通過怎披;
XCTAssert(expression, format...)當expression求值為TRUE時通過;
XCTAssertTrue(expression, format...)當expression求值為TRUE時通過瓶摆;
XCTAssertFalse(expression, format...)當expression求值為False時通過凉逛;
XCTAssertEqualObjects(a1, a2, format...)判斷相等,[a1 isEqual:a2]值為TRUE時通過群井,其中一個不為空時状飞,不通過;
XCTAssertNotEqualObjects(a1, a2, format...)判斷不等蝌借,[a1 isEqual:a2]值為False時通過昔瞧;
XCTAssertEqual(a1, a2, format...)判斷相等(當a1和a2是 C語言標量指蚁、結(jié)構(gòu)體或聯(lián)合體時使用,實際測試發(fā)現(xiàn)NSString也可以)菩佑;
XCTAssertNotEqual(a1, a2, format...)判斷不等(當a1和a2是 C語言標量、結(jié)構(gòu)體或聯(lián)合體時使用)凝化;
XCTAssertEqualWithAccuracy(a1, a2, accuracy, format...)判斷相等稍坯,(double或float類型)提供一個誤差范圍,當在誤差范圍(+/-accuracy)以內(nèi)相等時通過測試搓劫;
XCTAssertNotEqualWithAccuracy(a1, a2, accuracy, format...) 判斷不等瞧哟,(double或float類型)提供一個誤差范圍,當在誤差范圍以內(nèi)不等時通過測試枪向;
XCTAssertThrows(expression, format...)異常測試勤揩,當expression發(fā)生異常時通過;反之不通過秘蛔;(很變態(tài)) XCTAssertThrowsSpecific(expression, specificException, format...) 異常測試陨亡,當expression發(fā)生specificException異常時通過;反之發(fā)生其他異成钤保或不發(fā)生異常均不通過负蠕;
XCTAssertThrowsSpecificNamed(expression, specificException, exception_name, format...)異常測試,當expression發(fā)生具體異常倦畅、具體異常名稱的異常時通過測試遮糖,反之不通過;
XCTAssertNoThrow(expression, format…)異常測試叠赐,當expression沒有發(fā)生異常時通過測試欲账;
XCTAssertNoThrowSpecific(expression, specificException, format...)異常測試屡江,當expression沒有發(fā)生具體異常、具體異常名稱的異常時通過測試赛不,反之不通過盼理;
XCTAssertNoThrowSpecificNamed(expression, specificException, exception_name, format...)異常測試,當expression沒有發(fā)生具體異常俄删、具體異常名稱的異常時通過測試宏怔,反之不通過
特別注意下XCTAssertEqualObjects和XCTAssertEqual。
XCTAssertEqualObjects(a1, a2, format...)的判斷條件是[a1 isEqual:a2]是否返回一個YES畴椰。XCTAssertEqual(a1, a2, format...)的判斷條件是a1 == a2是否返回一個YES臊诊。對于后者,如果a1和a2都是基本數(shù)據(jù)類型變量斜脂,那么只有a1 == a2才會返回YES抓艳。
示例如下:
- 斷言:
- (void)testExample {
//設置變量和預期效果
NSUInteger a = 10;NSUInteger b = 15;
NSUInteger c = 24;
//執(zhí)行方法得到
NSUInteger sum = [self sum:a b:b];
//斷言判定實際值和預期是否符合
XCTAssertEqual(sum, c,@"測試不通過");
}
-(NSUInteger)sum:(NSUInteger)a b:(NSUInteger)b{
return a+b;
}
- 性能
關于性能測試:設置baseline(基準)和stddev(標準偏差)來判斷方法是否能通過性能測試。
就比如說關于網(wǎng)絡請求通常我們給出一個標準帚戳,多長時間內(nèi)請求到結(jié)果玷或,能夠接受的偏差是多少。那么就可以設置一個基準盒偏差來驗證這個請求片任。
- 異步
官方文檔中給出
- (void)testDocumentOpening
{
// 創(chuàng)建一個expectation對象
XCTestExpectation *documentOpenExpectation = [self expectationWithDescription:@"document open"];
NSURL *URL = [[NSBundle bundleForClass:[self class]]
URLForResource:@"TestDocument" withExtension:@"mydoc"];
UIDocument *doc = [[UIDocument alloc] initWithFileURL:URL];
[doc openWithCompletionHandler:^(BOOL success) {
XCTAssert(success);
// assert成功后 便會調(diào)用expectation的fulfill方法偏友,來觸發(fā)下面的handler
[documentOpenExpectation fulfill];
}];
// 在case最后使用這一方法,此時單測程序會阻塞到這里对供;除非達到了超時時間(秒單位)或者是回調(diào)函數(shù)中調(diào)用了fulfill位他,單測程序才會結(jié)束
// 若是超時情況,則認為case失敳 鹅髓;若通過expectation的fulfill觸發(fā),則case通過
[self waitForExpectationsWithTimeout:1 handler:^(NSError *error) {
[doc closeWithCompletionHandler:nil];
}];
}
- (void)testAsynExample {
XCTestExpectation *exp = [self expectationWithDescription:@"這里可以是操作出錯的原因描述京景。窿冯。。"];
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
[queue addOperationWithBlock:^{
//模擬這個異步操作需要2秒后才能獲取結(jié)果确徙,比如一個異步網(wǎng)絡請求
sleep(2);
//模擬獲取的異步操作后醒串,獲取結(jié)果,判斷異步方法的結(jié)果是否正確
XCTAssertEqual(@"a", @"a");
//如果斷言沒問題米愿,就調(diào)用fulfill宣布測試滿足
[exp fulfill];
}];
//設置延遲多少秒后厦凤,如果沒有滿足測試條件就報錯
[self waitForExpectationsWithTimeout:3 handler:^(NSError * _Nullable error) {
if (error) {
NSLog(@"Timeout Error: %@", error);
}
}];
}
這個測試肯定是通過的,因為設置延遲為3秒育苟,而異步操作2秒就除了一個正確的結(jié)果较鼓,并宣布了條件滿足 [exp fulfill]
,但是當我們把延遲改成1秒,這個測試用例就不會成功博烂,錯誤原因是 expectationWithDescription:@"這里可以是操作出錯的原因描述香椎。。禽篱。
異步測試除了使用 expectationWithDescription
以外畜伐,還可以使用 expectationForPredicate
和expectationForNotification
下面這個例子使用expectationForPredicate
測試方法,代碼來自于AFNetworking躺率,用于測試backgroundImageForState
方法
- (void)testThatBackgroundImageChanges {
XCTAssertNil([self.button backgroundImageForState:UIControlStateNormal]);
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(UIButton * _Nonnull button, NSDictionary<NSString *,id> * _Nullable bindings) {
return [button backgroundImageForState:UIControlStateNormal] != nil;
}];
[self expectationForPredicate:predicate
evaluatedWithObject:self.button
handler:nil];
[self waitForExpectationsWithTimeout:20 handler:nil];
}
利用謂詞計算玛界,button是否正確的獲得了backgroundImage,如果正確20秒內(nèi)正確獲得則通過測試悼吱,否則失敗慎框。
expectationForNotification 方法 ,該方法監(jiān)聽一個通知,如果在規(guī)定時間內(nèi)正確收到通知則測試通過。
- (void)testAsynExample1 {
[self expectationForNotification:(@"監(jiān)聽通知的名稱xxx") object:nil handler:nil];
[[NSNotificationCenter defaultCenter]postNotificationName:@"監(jiān)聽通知的名稱xxx" object:nil];
//設置延遲多少秒后后添,如果沒有滿足測試條件就報錯
[self waitForExpectationsWithTimeout:3 handler:nil];
}
這個例子也可以用expectationWithDescription
實現(xiàn),只是多些很多代碼而已笨枯,但是這個可以幫助你更好的理解 expectationForNotification
方法和 expectationWithDescription
的區(qū)別。同理遇西,expectationForPredicate
方法也可以使用expectationWithDescription
實現(xiàn)馅精。
func testAsynExample1() {
let expectation = expectationWithDescription("監(jiān)聽通知的名稱xxx")
let sub = NSNotificationCenter.defaultCenter().addObserverForName("監(jiān)聽通知的名稱xxx", object: nil, queue: nil) { (not) -> Void in
expectation.fulfill()
}
NSNotificationCenter.defaultCenter().postNotificationName("監(jiān)聽通知的名稱xxx", object: nil)
waitForExpectationsWithTimeout(1, handler: nil)
NSNotificationCenter.defaultCenter().removeObserver(sub)
}
測試失敗的調(diào)試工具
- 斷點調(diào)試
添加測試斷點

當測試方法發(fā)布故障斷言時,此斷點停止測試運??行粱檀。這樣可以通過在測試代碼中設置的斷點停止執(zhí)行洲敢,快速找到存在問題的位置。

使用命令行運行測試
使用Xcode命令行工具梧税,你可以使用腳本自動構(gòu)建和測試你的項目沦疾。
- 例如:
測試本地OS X的MyApp
xcodebuild test -project MyAppProject.xcodeproj -scheme MyApp -destination 'platform=OS X,arch=x86_64'
模擬器上運行称近,使用模擬器可以應對不同的外形因素和操作系統(tǒng)版本:
例如 > xcodebuild test -project MyAppProject.xcodeproj -scheme MyApp -destination 'platform=iOS Simulator,name=iPhone,0S=7.0'
測試機上運行:
> xcodebuild test -project MyAppProject.xcodeproj -scheme MyApp -destination 'platform=iOS,name=Development test6s
destination參數(shù)可以連接在一起第队,一個命令在指定共享配置目標上執(zhí)行測試。例如刨秆,下面的命令鏈將前面三個例子結(jié)合在一起變成一個命令:
xcodebuild test -project MyAppProject.xcodeproj -scheme MyApp-destination 'platform=OS X,arch=x86_64'-destination 'platform=iOS,name=Development iPod touch'-destination 'platform=Simulator,name=iPhone,OS=9.0'
如果測試失敗凳谦,xcodebuild返回非零退出代碼。
關于命令行運行測試衡未,你需要知道一些基本要素尸执。關于xcodebuild的詳細信息,在終端應用窗口使用man:
man xcodebuild
輔助工具

Test Callers category 缓醋。如果你剛剛修復導致應用測試失敗的方法如失,也許你希望檢查其他測試中調(diào)用的該方法是否運行成功。在源代碼編輯器中有問題的方法中送粱,打開assistant editor并從菜單中選擇 Test Classes category褪贵。在彈窗菜單中可以導航到任何調(diào)用它的測試方法中,運行這些測試方法確保回歸測試脆丁。
Test Classes category世舰。它與Test Callers category.類似,但顯示的是有測試方法的類的列表槽卫,可以導航到你正在編輯的類跟压。
代碼覆蓋
代碼覆蓋率是Xcode7的功能,可以在視覺上看到和衡量你的代碼測試覆蓋率歼培。有了代碼覆蓋率震蒋,你可以確定測試是否符合你的預期。
啟用代碼覆蓋率
Xcode的代碼覆蓋率由LLVM支持的測試操作躲庄。當你啟用代碼覆蓋率喷好,LLVM基于方法和函數(shù)調(diào)用的頻率來收集覆蓋數(shù)據(jù)。代碼覆蓋率選項可以收集單元測試和UI測試正確性和性能數(shù)據(jù)读跷,
編輯scheme的測試操作可以啟用代碼覆蓋率梗搅。
- 在scheme編輯菜單中選擇Edit Scheme。
 - 選擇測試操作效览。
- 啟用代碼覆蓋復選框以收集覆蓋數(shù)據(jù)无切。

注意:收集代碼覆蓋率數(shù)據(jù)會導致性能損耗。無論損耗是否顯著丐枉,它均會影響執(zhí)行代碼的線性方式哆键,因此在測試運行中啟用代碼覆蓋率,性能結(jié)果依然具有可比性瘦锹。然而籍嘹,當你正在認真評估測試程序性能時,你應該考慮是否啟用代碼覆蓋率弯院。
- 代碼覆蓋率如何符合測試
代碼覆蓋率是用來衡量測試價值的工具辱士。它回答了以下問題
當你運行測試時,什么代碼真正運行听绳?
多少測試才算足夠颂碘?
換句話說,你是否設計足夠的測試確保你所有的代碼都檢查了正確性和性能椅挣?
代碼的哪部分沒有被測試头岔?
在測試運行完成后,Xcode采用LLVM覆蓋數(shù)據(jù)并在報告導航中創(chuàng)建覆蓋率報告鼠证,參見覆蓋率面板峡竣。它顯示了測試的摘要信息,源文件和源文件中的方法列表以及每個文件中的覆蓋百分比量九。

源代碼編輯器展示了文件中代碼的行數(shù)并高亮未執(zhí)行的代碼适掰。它高亮需要覆蓋的代碼區(qū)域而非已經(jīng)覆蓋的區(qū)域。
例如,將指針放在-[Calculator input:]
方法上攻谁,將顯示一個按鈕稚伍,索引到源代碼。

覆蓋注釋在右邊顯示戚宦,顯示了在測試中代碼某個特定部分被執(zhí)行的次數(shù)个曙。例如:

input:
方法,在測試中被頻繁調(diào)用受楼。然而垦搬,有部分方法并未被調(diào)用。在源代碼編輯器中有明顯的標記艳汽,如圖:

官方文檔:關于Xcode測試