目录
一、简介
单元测试是指开发者编写代码,去验证被测代码是否正确的一种手段,其实就是用代码去检测代码。合理的利用单元测试可以提高软件的质量。
二、苹果自带的XCTest
1.创建我们的工程,勾选 Include Unit Tests
2.创建单元测试类
3.命名和继承的父类
UserInfoModelTest.m
中的代码:
+ (void)setUp {
[super setUp];
}
+ (void)tearDown {
[super tearDown];
}
// 单元测试开始前调用
- (void)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.
}
// 测试代码可以写到以test开头的方法中 并且test开头的方法左边会生成一个菱形图标,点击即可运行检测当前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.
// 测试的基本结构,三段式
// 1、Given 创造测试条件。数据初始化的对象,值。可以使用 OCMock 三方库。
UserInfoModel *userInfoModel = [UserInfoModel new];
// 2、When 测试方法的参数
userInfoModel.age = @10; // 模拟合法年龄( 0 < age < 80 认为是合法年龄)
// 3、Then 断言,是否符合预期
XCTAssert(userInfoModel.age.integerValue > 0, "姓名大于0 - 合法");
XCTAssert(userInfoModel.age.integerValue < 80, "姓名小于80 - 合法");
}
// 测试性能
- (void)testPerformanceExample {
// This is an example of a performance test case.
[self measureBlock:^{
// Put the code you want to measure the time of here.
}];
}
测试方法会以 -(void)testXXX
方式命名。
我们一起看一下方法的调用顺序,可以使用快捷键 command + U
- 先调用
+ (void)setUp
方法,加载我们的测试类 - 调用
- (void)setUp
,单元测试开始前调 - 调用
- (void)testExample
,测试代码可以写到以test开头的方法中 并且test开头的方法左边会生成一个菱形图标,点击即可运行检测当前test方法内的代码; - 调用
- (void)tearDown
,单元测试结束前调用; - 调用
- (void)setUp
,重置对象等的信息,继续测试 - 调用
- (void)testPerformanceExample
,测试性能 - 调用
- (void)tearDown
- 最后调用
+ (void)tearDown
我们可以看到 - (void)setUp
、- (void)tearDown
会重复调用,分别表示测试开始重置对象或数据,测试结束。
另外,测试代码可以写到以 test
开头的方法中 并且 test
开头的方法左边会生成一个菱形图标(如下图),点击即可运行检测当前test方法内的代码。
test
方法中的代码一般为三段式结构,如上代码。
下面我们模拟对 UserInfoMode
类中的 age 属性进行测试,具体实现可以看上面的代码。
我们主要是设置一些 断言
,来判断代码是否符合我们的预期,是否符合逻辑。
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语言标量、结构体或联合体时使用,实际测试发现NSString也可以);
XCTAssertNotEqual(a1, a2, format...)判断不等(当a1和a2是 C语言标量、结构体或联合体时使用);
XCTAssertEqualWithAccuracy(a1, a2, accuracy, format...)判断相等,(double或float类型)提供一个误差范围,当在误差范围(+/-accuracy)以内相等时通过测试;
XCTAssertNotEqualWithAccuracy(a1, a2, accuracy, format...) 判断不等,(double或float类型)提供一个误差范围,当在误差范围以内不等时通过测试;
XCTAssertThrows(expression, format...)异常测试,当expression发生异常时通过;反之不通过;(很变态)
XCTAssertThrowsSpecific(expression, specificException, format...) 异常测试,当expression发生specificException异常时通过;反之发生其他异常或不发生异常均不通过;
XCTAssertThrowsSpecificNamed(expression, specificException, exception_name, format...)异常测试,当expression发生具体异常、具体异常名称的异常时通过测试,反之不通过;
XCTAssertNoThrow(expression, format…)异常测试,当expression没有发生异常时通过测试;
XCTAssertNoThrowSpecific(expression, specificException, format...)异常测试,当expression没有发生具体异常、具体异常名称的异常时通过测试,反之不通过;
XCTAssertNoThrowSpecificNamed(expression, specificException, exception_name, format...)异常测试,当expression没有发生具体异常、具体异常名称的异常时通过测试,反之不通过
以上是XCTest的基本用法,下面我们说一下- (void)testPerformanceExample
性能测试。将要测试执行时间的代码放到 block 块内
// 测试性能
- (void)testPerformanceExample {
NSLog(@"%s", __func__);
// This is an example of a performance test case.
[self measureBlock:^{
// Put the code you want to measure the time of here.
NSMutableArray *testArray = [[NSMutableArray alloc] init];
for (int i = 0; i < 10000; i ++) {
NSObject *obj = [[NSObject alloc] init];
[testArray addObject:obj];
}
}];
}
控制台输出结果:
Test Case '-[UserInfoModelTest testPerformanceExample]' measured [Time, seconds] average: 0.015, relative standard deviation: 61.727%, values: [0.003263, 0.022181, 0.019327, 0.002817, 0.023823, 0.004887, 0.026164, 0.022138, 0.004508, 0.021344], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100
从这里我们可以获知在一个for循环重复的代码,程序会运行10次,取一个平均运行时间值,average: 0.015这个就是平均时间0.015秒。
现在我们知道了测量一个函数的运行时间,到底这个函数效率高不高可以使用testPerformanceExample方法,但是在这之前我们怎么测试函数性能呢?我们可以使用NSTimeInterval来做,根据时间差的打印来分析,具体用法如下代码:
// 测试性能
- (void)testPerformanceExample {
[self measureBlock:^{
// CACurrentMediaTime() 是手机从开机一直到当前所经过的秒数。
NSTimeInterval startTime = CACurrentMediaTime();
NSMutableArray * mutArray = [[NSMutableArray alloc] init];
for (int i = 0; i < 10000; i++) {
NSObject * object = [[NSObject alloc] init];
[mutArray addObject:object];
}
NSLog(@"耗时:%f",CACurrentMediaTime() - startTime);
}];
}
输出结果:
2019-02-13 12:49:36.298823+0800 CocoapodsDemo[7824:2125978] 耗时:0.031548
2019-02-13 12:49:36.335230+0800 CocoapodsDemo[7824:2125978] 耗时:0.033513
2019-02-13 12:49:36.363585+0800 CocoapodsDemo[7824:2125978] 耗时:0.001930
2019-02-13 12:49:36.382447+0800 CocoapodsDemo[7824:2125978] 耗时:0.017103
2019-02-13 12:49:36.401863+0800 CocoapodsDemo[7824:2125978] 耗时:0.017622
2019-02-13 12:49:36.421803+0800 CocoapodsDemo[7824:2125978] 耗时:0.017795
2019-02-13 12:49:36.444744+0800 CocoapodsDemo[7824:2125978] 耗时:0.020235
2019-02-13 12:49:36.466671+0800 CocoapodsDemo[7824:2125978] 耗时:0.019422
2019-02-13 12:49:36.487286+0800 CocoapodsDemo[7824:2125978] 耗时:0.018172
2019-02-13 12:49:36.506918+0800 CocoapodsDemo[7824:2125978] 耗时:0.001468
参考:
浅谈iOS单元测试