單元測(cè)試分為3種:
- 邏輯測(cè)試:測(cè)試邏輯方法
- 異步測(cè)試:測(cè)試耗時(shí)方法(用來(lái)測(cè)試包含多線程的方法)
- 性能測(cè)試:測(cè)試某一方法運(yùn)行所消耗的時(shí)間
本節(jié)介紹邏輯測(cè)試,直接創(chuàng)建一個(gè)項(xiàng)目吧恃,并選中Include Unit Tests選項(xiàng)虾啦。
創(chuàng)建工程時(shí)選中Unit Tests.png
1.新建一個(gè)Person類,提供字典轉(zhuǎn)模型的方法痕寓,這個(gè)方法就是需要進(jìn)行單元測(cè)試的:
+ (instancetype)personWithDict:(NSDictionary *)dict;//字典轉(zhuǎn)模型方法
字典轉(zhuǎn)模型方法的單元測(cè)試.png
原始的Person.m文件:
#import "Person.h"
@implementation Person
+ (instancetype)personWithDict:(NSDictionary *)dict {
Person *obj = [[self alloc] init];
[obj setValuesForKeysWithDictionary:dict];
return obj;
}
@end
2.下面,我們打開單元測(cè)試Tests黃色虛擬文件夾,然后Command+N 創(chuàng)建一個(gè)單元測(cè)試文件溜在。文件命名格式:類名+Tests锄弱,繼承自:XCTestCase (如下圖示例)
新建一個(gè)Unit Test文件.png
單元測(cè)試文件的命名格式.png
創(chuàng)建完畢,可以看到Tests文件里有如下4個(gè)方法:
系統(tǒng)自帶UnitTest文件案例.png
單元測(cè)試開始前礼仗,先了解幾個(gè)知識(shí)點(diǎn):
- 單元測(cè)試是以代碼測(cè)試代碼
- 紅燈/綠燈迭代測(cè)試開發(fā)
- 自己建立
測(cè)試用例(使用例子數(shù)據(jù)吐咳,專門檢查邊界點(diǎn))
- 單元測(cè)試不是靠 NSLog 來(lái)測(cè)試,NSLog 是程序員用眼睛看的笨辦法元践。
使用斷言
來(lái)測(cè)試的韭脊,提前預(yù)判條件必須滿足!
XCTAssert(expression, ...)
XCTAssert(條件, 不滿足條件的描述)
3.準(zhǔn)備工作已完成单旁,下面正式開始單元測(cè)試:
導(dǎo)入Person.h頭文件沪羔,仿照文件自帶的 - (void)testExample {} 格式,寫這樣一個(gè)邏輯測(cè)試方法:- (void)testNewPerson {} 象浑,然后Command+S保存蔫饰,左邊會(huì)出現(xiàn)菱形白色方框琅豆,它就是執(zhí)行單元調(diào)試的按鈕
??,在調(diào)試過(guò)程中篓吁,我們需要反復(fù)點(diǎn)擊這個(gè)按鈕去調(diào)試趋距。方法里書寫測(cè)試代碼。
PersonTests.m文件:
// 邏輯測(cè)試方法
- (void)testNewPerson {
// 1.測(cè)試 name和age 是否一致
[self checkPersonWithDict:@{@"name":@"zhou", @"age":@30}];
/** 2.測(cè)試出 age 不符合實(shí)際越除,那么需要在字典轉(zhuǎn)模型方法中對(duì)age加以判斷:
if (obj.age <= 0 || obj.age >= 130) {
obj.age = 0;
}
*/
[self checkPersonWithDict:@{@"name":@"zhang",@"age":@200}];
// 3.測(cè)試出 name 為nil的情況节腐,因此在XCTAssert里添加條件:“person.name == nil“
[self checkPersonWithDict:@{}];
// 4.測(cè)試出 Person類中沒有 title 這個(gè)key,在字典轉(zhuǎn)模型方法中實(shí)現(xiàn):- (void)setValue:(id)value forUndefinedKey:(NSString *)key {}
[self checkPersonWithDict:@{@"name":@"zhou", @"age":@30, @"title":@"boss"}];
// 5.總體再驗(yàn)證一遍摘盆,結(jié)果Build Succeeded翼雀,測(cè)試全部通過(guò)
[self checkPersonWithDict:@{@"name":@"zhou", @"age":@-1, @"title":@"boss"}];
// 到目前為止 Person 的 工廠方法測(cè)試完成!?
}
// 根據(jù)字典檢查新建的 person 信息
- (void)checkPersonWithDict:(NSDictionary *)dict {
Person *person = [Person personWithDict:dict];
NSLog(@"%@",person);
// 獲取字典中的信息
NSString *name = dict[@"name"];
NSInteger age = [dict[@"age"] integerValue];
// 1.檢查名字
XCTAssert([name isEqualToString:person.name] || person.name == nil, @"姓名不一致");
// 2.檢查年齡
if (person.age > 0 && person.age < 130) {
XCTAssert(age == person.age, @"年齡不一致");
} else {
XCTAssert(person.age == 0, @"年齡超限");
}
}
最好調(diào)試修改后的Person.m文件:
#import "Person.h"
@implementation Person
+ (instancetype)personWithDict:(NSDictionary *)dict {
Person *obj = [[self alloc] init];
[obj setValuesForKeysWithDictionary:dict];
// 預(yù)防處理age超限
if (obj.age <= 0 || obj.age >= 130) {
obj.age = 0;
}
return obj;
}
// 預(yù)防處理沒有找到的key
- (void)setValue:(id)value forUndefinedKey:(NSString *)key {}
@end
注意事項(xiàng):
- 不是所有的方法都需要測(cè)試孩擂。
例如:私有方法不需要測(cè)試狼渊!只有暴露在 .h 中的方法需要測(cè)試!面向?qū)ο笥幸粋€(gè)原則:開閉原則类垦! - 所有跟 UI 有關(guān)的都不需要測(cè)試狈邑,也不好測(cè)試。
把業(yè)務(wù)邏輯
代碼封裝出來(lái)蚤认!變成可以測(cè)試的代碼米苹,讓程序更加健壯! - 一般而言砰琢,代碼的覆蓋度大概在 50% ~ 70%
從github上得知:YYModel測(cè)試覆蓋度為83%蘸嘶,AFNetworking測(cè)試覆蓋度為77%,兩者都是比較高的陪汽。