參考自:?iOS 單元測試之XCTest詳解
原創(chuàng)blog谁鳍,轉(zhuǎn)載請注明出處?
歡迎關(guān)注我的iOS-SDK詳解專欄?
http://blog.csdn.net/column/details/huangwenchen-ios-sdk.html
前言:測試是一個好的App不可缺少的部分藤为。每一個App都是由一個個小的功能組合到一起的备绽。而這些小的功能又是由一個個函數(shù)或者說算法組合到一起的。單元測試就是對這些小的功能或者函數(shù)進(jìn)行測試侥祭,良好的單元測試會讓代碼的健壯性提高很多秩霍。XCTest就是XCode為我們提供的一個框架,它提供了各個層次的測試单匣。
每個XCode創(chuàng)建iOS的工程中都有一個叫做”工程名Tests”的分組夕凝,這個分組里就是XCTestCase的子類,XCTest中的測試類都是繼承自XCTestCase户秤。?
例如新建一個工程码秉,命名為Demo,就能看到如圖?
看一下這個自動創(chuàng)建的文件里都包含了哪些內(nèi)容
#import ?
@interface?DemoTests : XCTestCase?
@end
?@implementation?DemoTests
- (void)setUp {
????[super?setUp];?
????//?Put setup code here. This method?iscalled 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.
????XCTAssert(YES, @"Pass");
}
- (void)testPerformanceExample {?
????//?This?is?an example?of?a performance test?case. [self?measureBlock:^{?
????//?Put the code you want to measure the time?of?here. }];
}
?@end
XCTest中所有的測試用例的命名都是以test開頭的鸡号。例如上文中的
- (void)testExample {
//?This?is?an example?of?a functional test?case.
????XCTAssert(YES, @"Pass");
}
Setup是在所有測試用例運行之前運行的函數(shù)转砖,在這個測試用例里進(jìn)行一些通用的初始化工作
tearDown是在所有的測試用例都執(zhí)行完畢后執(zhí)行的
測試用例的導(dǎo)航如圖,在測試用例的導(dǎo)航里,我們可以運行一組測試用例府蔗,也可以運行一個單獨的測試用例?
可以鼠標(biāo)右鍵來新建一組測試用例晋控。?
也可以為測試用例添加失敗斷點來方便我們調(diào)試?
通過測試導(dǎo)航欄可以查看到測試結(jié)果
通過Report導(dǎo)航欄可以看到更詳細(xì)的測試結(jié)果
點擊測試用例后面的箭頭,可以跳轉(zhuǎn)到測試用例的代碼姓赤。
例如赡译,新建一個類命名為Model,他有這個方法用來生成10以內(nèi)的隨機數(shù)不铆。
- (NSInteger)randomLessThanTen{
????return arc4random()%10;
}
于是蝌焚,測試方法為
-(void)testModelFunc_randomLessThanTen{
????Model * model = [[Model alloc] init];
????NSInteger num = [model randomLessThanTen];
????XCTAssert(num<10,@"num should less than 10");
}
我們點擊如圖的左邊圖標(biāo)單獨運行這個測試用例,當(dāng)然也可以在上文我提到的導(dǎo)航欄里單獨運行誓斥。?
然后會看到輸出表示這個測試用例通過
Test Suite?'Selected tests'?started?at?2015-06-28?05:24:56?+0000?
Test Suite?'DemoTests.xctest'?started?at?2015-06-28?05:24:56?+0000?
Test Suite?'DemoTests'?started?at?2015-06-28?05:24:56?+0000?
Test Case?'-[DemoTests testModelFunc_randomLessThanTen]'?started.
Test Case?'-[DemoTests testModelFunc_randomLessThanTen]'?passed (0.000?seconds).
Test Suite?'DemoTests'?passed?at?2015-06-28?05:24:56?+0000.?Executed?1?test,?with?0?failures (0?unexpected)?in?0.000?(0.001)?seconds?
Test Suite?'DemoTests.xctest'?passed?at?2015-06-28?05:24:56?+0000.?Executed?1?test,?with?0?failures (0?unexpected)?in?0.000?(0.001)?seconds?
Test Suite?'Selected tests'?passed?at?2015-06-28?05:24:56?+0000.
如何判斷一個測試用例成功或者失敗呢只洒?XCTest使用斷言來實現(xiàn)。?
最基本的斷言?
表示如果expression滿足岖食,則測試通過红碑,否則對應(yīng)format的錯誤。
XCTAssert(expression,?format...)
還有一個用來直接Fail的斷言
XCTFail(format...)
其他一些常用的斷言:
XCTAssertTrue(expression,?format...)
XCTAssertFalse(expression,?format...)
XCTAssertEqual(expression1, expression2,?format...)
XCTAssertNotEqual(expression1, expression2,?format...)
XCTAssertEqualWithAccuracy(expression1, expression2, accuracy,?format...)
XCTAssertNotEqualWithAccuracy(expression1, expression2, accuracy,?format...)
XCTAssertNil(expression,?format...) XCTAssertNotNil(expression,?format...)
所謂性能測試泡垃,主要就是評估一段代碼的運行時間析珊,XCTest的性能的測試?yán)萌缦赂袷?/p>
對于性能測試,每一個測試用例每次會運行10次蔑穴。
- (void)testPerformanceExample {?
????/?This?is?an example?of?a performance test?case.
????[self?measureBlock:^{?
????/?Put the code you want to measure the time?of?here.
????}];
}
例如忠寻,我要評估一段代碼,循環(huán)打印NSLog 10000次存和。?
這段代碼如下奕剃,這段代碼我放在UIImage的類別里。
- (void)testPerformanceExample {?
????// This is an example of a performance test case.
?????[self measureBlock:^{?
????????for?(NSInteger?index?=?0;?index?<?10000;?index?++) {
????????????NSLog(@"%ld",index);
????????}?
????????// Put the code you want to measure the time of here.?
????}];
}
我們都知道捐腿,測試要么成功纵朋,要么失敗,那么就引入了一個關(guān)鍵的問題
性能測試的時候茄袖,如何判一個性能測試case是成功還是失敗呢操软?
我們先通過上文的方式,只運行一次這個測試用例宪祥。然后看看結(jié)果和輸出(這個測試用例跑的很慢聂薪,別著急)
Test Case?'-[ModelTests testPerformanceExample]'?failed (37.432?seconds).
Test Suite?'ModelTests'?failed?at?2017-02-19?09:57:26.210. Executed?1?test,?with?1?failure (0?unexpected)?in?37.432?(37.433)?seconds
?Test Suite?'ToDoTests.xctest'?failed?at?2017-02-19?09:57:26.211. Executed?1?test,?with?1?failure (0?unexpected)?in?37.432?(37.434)?seconds
?Test Suite?'Selected tests'?failed?at?2017-02-19?09:57:26.211. Executed?1?test,?with?1?failure (0?unexpected)?in?37.432?(37.437)?seconds?
Test session?log: /Users/hl/Library/Developer/Xcode/DerivedData/ToDo-bbcdkwvzbmyznocgystdcavfakca/Logs/Test/98E0FA82-BACC-4361-AF39-E0734F73A545/Session-ToDoTests-2017-02-19_095641-jm2eKF.log
然后,你會發(fā)現(xiàn)測試失敗了蝗羊!這是因為我們沒有給性能測試一個參考時間藏澳。?
我們點擊圖中的的第二個叉箭頭
然后,看到如圖?
我們來看看這幾個參數(shù)都是啥意思:
Baseline 計算標(biāo)準(zhǔn)差的參考值
MAX STDD 最大允許的標(biāo)準(zhǔn)差
底部點擊1耀找,2…10可以看到每次運行的結(jié)果翔悠。
點擊Edit,我們點擊Average的右邊的Accept,來讓本次運行的平均值設(shè)置為baseline凉驻,然后然后MAX STDD改為40%腻要。再運行這個測試用例复罐,你會發(fā)現(xiàn)測試成功了涝登。
異步測試的邏輯如下,首先定義一個或者多個XCTestExpectation效诅,表示異步測試想要的結(jié)果胀滚。然后設(shè)置timeout,表示異步測試最多可以執(zhí)行的時間乱投。最后咽笼,在異步的代碼完成的最后,調(diào)用fullfill來通知異步測試滿足條件戚炫。
- (void)testAsyncFunction{?
????XCTestExpectation?* expectation = [self?expectationWithDescription:@"Just a demo ????expectation,should pass"];?
????//Async?function?when?finished call
????[expectation fullfill];
????[self?waitForExpectationsWithTimeout:10?handler:^(NSError?*error) {?//Do?something?when?time out }]; }
舉例
- (void)testAsyncFunction{
????XCTestExpectation * expectation = [self?expectationWithDescription:@"Just a demo expectation,should pass"];
? ?dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,?0), ^{ ????????sleep(1);?
????????NSLog(@"Async test");
????????XCTAssert(YES,"should pass");
????????[expectation fulfill];
????});
????[self?waitForExpectationsWithTimeout:10?handler:^(NSError?*error) {?
????????//Do something when time out
?????}];
}
測試結(jié)果
Test Suite?'Selected tests'?started?at?2015-06-28?05:49:43?+0000?
Test Suite?'DemoTests.xctest'?started?at?2015-06-28?05:49:43?+0000?
Test Suite?'DemoTests'?started?at?2015-06-28?05:49:43?+0000?
Test Case?'-[DemoTests testAsyncFunction]'?started.?2015-06-28?13:49:44.920
?Demo[2157:145428] Async test Test Case?'-[DemoTests testAsyncFunction]'?passed (1.006?seconds). Test Suite?'DemoTests'?passed?at?2015-06-28?05:49:44?+0000.?Executed?1?test,?with?0?failures (0?unexpected)?in?1.006?(1.007)?seconds?
Test Suite?'DemoTests.xctest'?passed?at?2015-06-28?05:49:44?+0000.?Executed?1?test,?with?0?failures (0?unexpected)?in?1.006?(1.009)?seconds?
Test Suite?'Selected tests'?passed?at?2015-06-28?05:49:44?+0000.
選擇Target剑刑,然后選擇Test模塊,然后勾選Gather coverage data
然后双肤,在report模塊中施掏,就能看到每一個.m文件的代碼覆蓋情況了。
更新:2017.02.19 增加代碼覆蓋率茅糜,對性能測試進(jìn)行詳細(xì)的講解七芭。
計劃下一篇會講解Mock 測試以及一些常用的Mock小工具。