OCUnit(即用XCTest進行測試
)蘋果自帶的測試框架,GHUnit是一個可視化的測試框架。(有了它,你可以點擊APP來決定測試哪個方法,并且可以點擊查看測試結果等。)OCMock就是模擬某個方法或者屬性的返回值,你可能會疑惑為什么要這樣做?使用用模型生成的模型對象,再傳進去不就可以了?答案是可以的,但是有特殊的情況。比如你測試的是方法A,方法A里面調用到了方法B,而且方法B是有參數傳入,但又不是方法A所提供。這時候,你可以使用OCMock來模擬方法B返回的值。(在不影響測試的情況下,就可以這樣去模擬。)除了這些,在沒有網絡的情況下,也可以通過OCMock模擬返回的數據。UITests就是通過代碼化來實現自動點擊界面,輸入文字等功能??咳斯げ僮鞯姆绞絹砀采w所有測試用例是非常困難的,尤其是加入新功能以后,舊的功能也要重新測試一遍,這導致了測試需要花非常多的時間來進行回歸測試,這里產生了大量重復的工作,而這些重復的工作有些是可以自動完成的,這時候UITests就可以幫助解決這個問題。
先來簡單的了解一下單元測試
使用快捷鍵Command+U,這個快捷鍵是全部測試。
也可以單個方法的測試:
表示測試通過就會出現綠色的菱形
熟悉單元測試類
UnitTestsDemoTests類是繼承與 XCTestCase的
- (void)setUp {
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
//每個test方法執行之前調用
}
- (void)tearDown {
// Put teardown code here. This method is called after the invocation of each test method in the class.
[super tearDown];
// 每個test方法執行之后調用
}
- (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的測試方法
}
//性能測試
- (void)testPerformanceExample {
// This is an example of a performance test case.
//主要檢測代碼的執行性能
[self measureBlock:^{
// Put the code you want to measure the time of here.
}];
}
Xcode7默認帶了測試性能的方法- (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);
}
}];
}
重復執行上面的代碼,會收集每次執行的時間,并計算出平均值,每次執行后會跟平均值進行比較,給你參考性的提示。
當我們把i的值后面增添一個0后:
XCode檢測到這一次運行,遠超過了平均值,給出了紅色的警告
自定義測試方法
- 自定義測試方法必須以test方法名開頭(testXXX),例如testExample
- 自定義方法必須為void返回類型
斷言
大部分的測試方法使用斷言決定的測試結果。所有斷言都有一個類似的形式:比較,表達式為真假,強行失敗等。
//通用斷言
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的斷言
舉個栗子
- (void)testExample {
//設置變量和設置預期值
NSUInteger a = 10;
NSUInteger b = 15;
NSUInteger expected = 24;
//執行方法得到實際值
NSUInteger actual = [self add:a b:b];
//斷言判定實際值和預期是否符合
XCTAssertEqual(expected, actual,@"add方法錯誤!");
}
-(NSUInteger)add:(NSUInteger)a b:(NSUInteger)b{
return a+b;
}
從這也能看出一個測試用例比較規范的寫法,1:定義變量和預期,2:執行方法得到實際值,3:斷言
性能測試
性能測試主要使用measureBlock
方法 ,用于測試一組方法的執行時間,通過設置baseline(基準)和stddev(標準偏差)來判斷方法是否能通過性能測試。
舉個栗子:
- (void)testPerformanceExample {
// This is an example of a performance test case.
[self measureBlock:^{
//Put the code you want to measure the time of here.
//你的性能測試的代碼放在這里
}];
}
直接執行方法,因為block中沒有內容,所以方法的執行時間為0.0s,如果我們把baseline設成0.05,偏差10%,是可以通過的測試的。但是如果設置如果我們把baseline為1,偏差10%,那測試會失敗,因為不滿足條件。
異步測試
測試異步方法時,因為結果并不是立刻獲得,所以在異步方法測試有一些特殊的方法和技巧。
舉個栗子:
- (void)testAsynExample {
XCTestExpectation *exp = [self expectationWithDescription:@"這里可以是操作出錯的原因描述。。。"];
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
[queue addOperationWithBlock:^{
//模擬這個異步操作需要2秒后才能獲取結果,比如一個異步網絡請求
sleep(2);
//模擬獲取的異步操作后,獲取結果,判斷異步方法的結果是否正確
XCTAssertEqual(@"a", @"a");
//如果斷言沒問題,就調用fulfill宣布測試滿足
[exp fulfill];
}]; //設置延遲多少秒后,如果沒有滿足測試條件就報錯
[self waitForExpectationsWithTimeout:3 handler:^(NSError * _Nullable error) {
if (error) {
NSLog(@"Timeout Error: %@", error); }
}];
}
這個測試肯定是通過的,因為設置延遲為3秒,而異步操作2秒就除了一個正確的結果,并宣布了條件滿足[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秒內正確獲得則通過測試,否則失敗。
expectationForNotification
方法 ,該方法監聽一個通知,如果在規定時間內正確收到通知則測試通過。
-(void)testAsynExample1 {
[self expectationForNotification:(@"監聽通知的名稱xxx") object:nil handler:nil];
[[NSNotificationCenter defaultCenter]postNotificationName:@"監聽通知的名稱xxx" object:nil];
//設置延遲多少秒后,如果沒有滿足測試條件就報錯
[self waitForExpectationsWithTimeout:3 handler:nil];
}
命令行測試
測試不僅可以在xcode中執行,也可以在命令行中執行,這個便于代碼持續集成和構建,在git提交中也編譯檢查代碼
如果你有development-enabled設備插入,你可以按照名稱或 id 調用他們。例如,如果你有一個名為”Development iPod touch”的 iPod 設備連接了測試的代碼,可以使用下面的命令來測試代碼> xcodebuild test -project MyAppProject.xcodeproj -scheme MyApp -destination 'platform=iOS,name=Development iPod touch
測試也可以在 iOS模擬器上運行。使用模擬器可以應對不同的外形因素和操作系統版本。例如> xcodebuild test -project MyAppProject.xcodeproj -scheme MyApp -destination 'platform=iOS Simulator,name=iPhone,0S=7.0'
-destination 參數可以被連接在一起,這樣你只需使用一個命令,就可以跨目標進行指定集成共享方案。例如,下面的命令把之前的三個例子合并到一個命令中
> 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'
關于更多xcodebuild的使用可以查看man手冊> man xcodebuild
執行測試快捷鍵
cmd + 5 切換到測試選項卡后會看到很多小箭頭,點擊可以單獨或整體測試
cmd + U 運行整個單元測試
注意點
使用pod的項目中,在XC測試框架中測試內容包括第三方包時,需要手動去設置Header Search Paths才能找到頭文件 ,還需要設置test target的PODS_ROOT。
參考閱讀
http://www.tuicool.com/articles/jUrqiqR