OCUnit(即用XCTest進(jìn)行測(cè)試
)蘋果自帶的測(cè)試框架盖奈,GHUnit是一個(gè)可視化的測(cè)試框架恶守。(有了它埋嵌,你可以點(diǎn)擊APP來(lái)決定測(cè)試哪個(gè)方法破加,并且可以點(diǎn)擊查看測(cè)試結(jié)果等。)OCMock就是模擬某個(gè)方法或者屬性的返回值莉恼,你可能會(huì)疑惑為什么要這樣做?使用用模型生成的模型對(duì)象拌喉,再傳進(jìn)去不就可以了速那?答案是可以的,但是有特殊的情況尿背。比如你測(cè)試的是方法A端仰,方法A里面調(diào)用到了方法B,而且方法B是有參數(shù)傳入田藐,但又不是方法A所提供荔烧。這時(shí)候,你可以使用OCMock來(lái)模擬方法B返回的值汽久。(在不影響測(cè)試的情況下鹤竭,就可以這樣去模擬。)除了這些景醇,在沒(méi)有網(wǎng)絡(luò)的情況下臀稚,也可以通過(guò)OCMock模擬返回的數(shù)據(jù)。UITests就是通過(guò)代碼化來(lái)實(shí)現(xiàn)自動(dòng)點(diǎn)擊界面三痰,輸入文字等功能吧寺。靠人工操作的方式來(lái)覆蓋所有測(cè)試用例是非常困難的散劫,尤其是加入新功能以后稚机,舊的功能也要重新測(cè)試一遍,這導(dǎo)致了測(cè)試需要花非常多的時(shí)間來(lái)進(jìn)行回歸測(cè)試获搏,這里產(chǎn)生了大量重復(fù)的工作赖条,而這些重復(fù)的工作有些是可以自動(dòng)完成的,這時(shí)候UITests就可以幫助解決這個(gè)問(wèn)題常熙。
先來(lái)簡(jiǎn)單的了解一下單元測(cè)試
使用快捷鍵Command+U纬乍,這個(gè)快捷鍵是全部測(cè)試。
也可以單個(gè)方法的測(cè)試:
表示測(cè)試通過(guò)就會(huì)出現(xiàn)綠色的菱形
熟悉單元測(cè)試類
UnitTestsDemoTests類是繼承與 XCTestCase的
- (void)setUp {
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
//每個(gè)test方法執(zhí)行之前調(diào)用
}
- (void)tearDown {
// Put teardown code here. This method is called after the invocation of each test method in the class.
[super tearDown];
// 每個(gè)test方法執(zhí)行之后調(diào)用
}
- (void)testExample {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
// 命名為Example的測(cè)試方法
}
//性能測(cè)試
- (void)testPerformanceExample {
// This is an example of a performance test case.
//主要檢測(cè)代碼的執(zhí)行性能
[self measureBlock:^{
// Put the code you want to measure the time of here.
}];
}
Xcode7默認(rèn)帶了測(cè)試性能的方法- (void)testPerformanceExample
- (void)testPerformanceExample {
// This is an example of a performance test case.
[self measureBlock:^{
// Put the code you want to measure the time of here.
for(int i=0;i<1000;i++) {
NSLog(@"%d",i);
}
}];
}
重復(fù)執(zhí)行上面的代碼症概,會(huì)收集每次執(zhí)行的時(shí)間蕾额,并計(jì)算出平均值,每次執(zhí)行后會(huì)跟平均值進(jìn)行比較彼城,給你參考性的提示诅蝶。
當(dāng)我們把i的值后面增添一個(gè)0后:
XCode檢測(cè)到這一次運(yùn)行,遠(yuǎn)超過(guò)了平均值募壕,給出了紅色的警告
自定義測(cè)試方法
- 自定義測(cè)試方法必須以test方法名開(kāi)頭(testXXX)调炬,例如testExample
- 自定義方法必須為void返回類型
斷言
大部分的測(cè)試方法使用斷言決定的測(cè)試結(jié)果。所有斷言都有一個(gè)類似的形式:比較舱馅,表達(dá)式為真假缰泡,強(qiáng)行失敗等。
//通用斷言
XCTAssert(expression, 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...)
XCTFail(format...) //直接Fail的斷言
舉個(gè)栗子
- (void)testExample {
//設(shè)置變量和設(shè)置預(yù)期值
NSUInteger a = 10;
NSUInteger b = 15;
NSUInteger expected = 24;
//執(zhí)行方法得到實(shí)際值
NSUInteger actual = [self add:a b:b];
//斷言判定實(shí)際值和預(yù)期是否符合
XCTAssertEqual(expected, actual,@"add方法錯(cuò)誤!");
}
-(NSUInteger)add:(NSUInteger)a b:(NSUInteger)b{
return a+b;
}
從這也能看出一個(gè)測(cè)試用例比較規(guī)范的寫(xiě)法棘钞,1:定義變量和預(yù)期缠借,2:執(zhí)行方法得到實(shí)際值,3:斷言
性能測(cè)試
性能測(cè)試主要使用measureBlock
方法 宜猜,用于測(cè)試一組方法的執(zhí)行時(shí)間泼返,通過(guò)設(shè)置baseline(基準(zhǔn))和stddev(標(biāo)準(zhǔn)偏差)來(lái)判斷方法是否能通過(guò)性能測(cè)試。
舉個(gè)栗子:
- (void)testPerformanceExample {
// This is an example of a performance test case.
[self measureBlock:^{
//Put the code you want to measure the time of here.
//你的性能測(cè)試的代碼放在這里
}];
}
直接執(zhí)行方法姨拥,因?yàn)閎lock中沒(méi)有內(nèi)容绅喉,所以方法的執(zhí)行時(shí)間為0.0s,如果我們把baseline設(shè)成0.05,偏差10%,是可以通過(guò)的測(cè)試的叫乌。但是如果設(shè)置如果我們把baseline為1柴罐,偏差10%,那測(cè)試會(huì)失敗憨奸,因?yàn)椴粷M足條件革屠。
異步測(cè)試
測(cè)試異步方法時(shí),因?yàn)榻Y(jié)果并不是立刻獲得膀藐,所以在異步方法測(cè)試有一些特殊的方法和技巧屠阻。
舉個(gè)栗子:
- (void)testAsynExample {
XCTestExpectation *exp = [self expectationWithDescription:@"這里可以是操作出錯(cuò)的原因描述。额各。。"];
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
[queue addOperationWithBlock:^{
//模擬這個(gè)異步操作需要2秒后才能獲取結(jié)果吧恃,比如一個(gè)異步網(wǎng)絡(luò)請(qǐng)求
sleep(2);
//模擬獲取的異步操作后虾啦,獲取結(jié)果,判斷異步方法的結(jié)果是否正確
XCTAssertEqual(@"a", @"a");
//如果斷言沒(méi)問(wèn)題痕寓,就調(diào)用fulfill宣布測(cè)試滿足
[exp fulfill];
}]; //設(shè)置延遲多少秒后傲醉,如果沒(méi)有滿足測(cè)試條件就報(bào)錯(cuò)
[self waitForExpectationsWithTimeout:3 handler:^(NSError * _Nullable error) {
if (error) {
NSLog(@"Timeout Error: %@", error); }
}];
}
這個(gè)測(cè)試肯定是通過(guò)的,因?yàn)樵O(shè)置延遲為3秒呻率,而異步操作2秒就除了一個(gè)正確的結(jié)果硬毕,并宣布了條件滿足[exp fulfill]
,但是當(dāng)我們把延遲改成1秒礼仗,這個(gè)測(cè)試用例就不會(huì)成功吐咳,錯(cuò)誤原因是expectationWithDescription:@"這里可以是操作出錯(cuò)的原因描述。元践。韭脊。
異步測(cè)試除了使用expectationWithDescription
以外,還可以使用expectationForPredicate和expectationForNotification
下面這個(gè)例子使用expectationForPredicate
測(cè)試方法单旁,代碼來(lái)自于AFNetworking沪羔,用于測(cè)試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];
}
利用謂詞計(jì)算,button是否正確的獲得了backgroundImage象浑,如果正確20秒內(nèi)正確獲得則通過(guò)測(cè)試蔫饰,否則失敗琅豆。
expectationForNotification
方法 ,該方法監(jiān)聽(tīng)一個(gè)通知,如果在規(guī)定時(shí)間內(nèi)正確收到通知?jiǎng)t測(cè)試通過(guò)。
-(void)testAsynExample1 {
[self expectationForNotification:(@"監(jiān)聽(tīng)通知的名稱xxx") object:nil handler:nil];
[[NSNotificationCenter defaultCenter]postNotificationName:@"監(jiān)聽(tīng)通知的名稱xxx" object:nil];
//設(shè)置延遲多少秒后篓吁,如果沒(méi)有滿足測(cè)試條件就報(bào)錯(cuò)
[self waitForExpectationsWithTimeout:3 handler:nil];
}
命令行測(cè)試
測(cè)試不僅可以在xcode中執(zhí)行趋距,也可以在命令行中執(zhí)行,這個(gè)便于代碼持續(xù)集成和構(gòu)建越除,在git提交中也編譯檢查代碼
如果你有development-enabled設(shè)備插入节腐,你可以按照名稱或 id 調(diào)用他們。例如摘盆,如果你有一個(gè)名為”Development iPod touch”的 iPod 設(shè)備連接了測(cè)試的代碼翼雀,可以使用下面的命令來(lái)測(cè)試代碼> xcodebuild test -project MyAppProject.xcodeproj -scheme MyApp -destination 'platform=iOS,name=Development iPod touch
測(cè)試也可以在 iOS模擬器上運(yùn)行。使用模擬器可以應(yīng)對(duì)不同的外形因素和操作系統(tǒng)版本孩擂。例如> xcodebuild test -project MyAppProject.xcodeproj -scheme MyApp -destination 'platform=iOS Simulator,name=iPhone,0S=7.0'
-destination 參數(shù)可以被連接在一起狼渊,這樣你只需使用一個(gè)命令,就可以跨目標(biāo)進(jìn)行指定集成共享方案类垦。例如狈邑,下面的命令把之前的三個(gè)例子合并到一個(gè)命令中
> xcodebuild test -project MyAppProject.xcodeproj -scheme MyApp
-destination 'platform=OS X,arch=x86_64'
-destination 'platform=iOS,name=Development iPod touch'
-destination 'platform=iOS Simulator,name=iPhone,0S=7.0'
關(guān)于更多xcodebuild的使用可以查看man手冊(cè)> man xcodebuild
執(zhí)行測(cè)試快捷鍵
cmd + 5 切換到測(cè)試選項(xiàng)卡后會(huì)看到很多小箭頭,點(diǎn)擊可以單獨(dú)或整體測(cè)試
cmd + U 運(yùn)行整個(gè)單元測(cè)試
注意點(diǎn)
使用pod的項(xiàng)目中蚤认,在XC測(cè)試框架中測(cè)試內(nèi)容包括第三方包時(shí)米苹,需要手動(dòng)去設(shè)置Header Search Paths才能找到頭文件 ,還需要設(shè)置test target的PODS_ROOT砰琢。
參考閱讀
http://www.tuicool.com/articles/jUrqiqR