点击运行- (void)testPersonalInformationAge方法左侧的菱形图标运行检查当前方法,会发现运行成功提示,如下图4。并且- (void)testPersonalInformationAge方法左边的菱形图标展示位绿颜色代表通过。


  
图4.检查通过

  接下来我们将- (void)testPersonalInformationAge方法内容改为如下测试用例,再次点击菱形图标看看效果如下图5。
  - (void)testPersonalInformationAge
  {
  ResponsePersonalInformation * response = [[ResponsePersonalInformation alloc] init];
  response.age = @"-1";      //模拟110非法年龄
  [self checkAge:response];
  }


  
图5.检查不通过

  控制台打印:
  Test Suite 'Selected tests' started at 2017-03-23 15:23:42.135
  Test Suite '单元测试demoTests.xctest' started at 2017-03-23 15:23:42.136
  Test Suite 'PersonalInformationTests' started at 2017-03-23 15:23:42.137
  Test Case '-[PersonalInformationTests testPersonalInformationAge]' started.  //以上是说明测试用例start开始
  /Users/xl10014/Desktop/单元测试demo/单元测试demoTests/PersonalInformationTests.m:45: error: -[PersonalInformationTests testPersonalInformationAge] : (([response.age integerValue] < 0) is true) failed - 姓名小于0岁-非法 //这里说明用例错误 并且定位到 文件具体行数 PersonalInformationTests.m:45行
  /Users/xl10014/Desktop/单元测试demo/单元测试demoTests/PersonalInformationTests.m:46: error: -[PersonalInformationTests testPersonalInformationAge] : (([response.age integerValue] >110) is true) failed - 姓名大于110岁-非法  //这里说明用例错误 并且定位到 文件具体行数 PersonalInformationTests.m:46行
  Test Case '-[PersonalInformationTests testPersonalInformationAge]' failed (0.015 seconds). //这里说明了测试用例失败 并指出测试时间花了0.015秒
  以上只是讲解了XCTest的基本用法,下面给大家说一下怎样利用XCTes进行性能测试,其实性能测试主要用到的是.m中的这个方法:
  - (void)testPerformanceExample {
  // This is an example of a performance test case.
  [self measureBlock:^{
  // Put the code you want to measure the time of here.
  }];
  }
  我们将要测量执行时间的代码放到testPerformanceExample方法内部的block中:
  - (void)testPerformanceExample {
  // This is an example of a performance test case.
  [self measureBlock:^{
  NSMutableArray * mutArray = [[NSMutableArray alloc] init];
  for (int i = 0; i < 9999; i++) {
  NSObject * object = [[NSObject alloc] init];
  [mutArray addObject:object];
  }
  }];
  }
  我在block中写了一个for循环执行9999次,然后点击方法左边的菱形图标,接着去看控制台打印信息如下:
  Test Suite 'Selected tests' started at 2017-03-23 15:54:41.488
  Test Suite '单元测试demoTests.xctest' started at 2017-03-23 15:54:41.489
  Test Suite 'PersonalInformationTests' started at 2017-03-23 15:54:41.490
  Test Case '-[PersonalInformationTests testPerformanceExample]' started.
  我们可以从中获取到有价值的信息: measured [Time, seconds] average: 0.002, relative standard deviation: 4.830%, values: [0.002294, 0.002094, 0.002255, 0.002304, 0.002295, 0.002052, 0.002055, 0.002135, 0.002352, 0.002224],从这里我们可以获知在一个for循环重复的代码,程序会运行10次,取一个平均运行时间值,average: 0.002这个是平均时间0.002秒。
  现在我们知道了测量一个函数的运行时间,到底这个函数效率高不高可以使用testPerformanceExample方法,但是在这之前我们怎么测试函数性能呢?我们可以使用NSTimeInterval来做,根据时间差的打印来分析,具体用法如下代码:
- (void)testPerformanceExample {
// This is an example of a performance test case.
[self measureBlock:^{
NSTimeInterval startTime = CACurrentMediaTime();
NSMutableArray * mutArray = [[NSMutableArray alloc] init];
for (int i = 0; i < 9999; i++) {
NSObject * object = [[NSObject alloc] init];
[mutArray addObject:object];
}
NSLog(@"%f",CACurrentMediaTime() - startTime);
}];
}
  三.单元测试的适用情况以及覆盖率
  在项目中很多人都不清楚到底测试用例的覆盖率是多少才合适,所以导致有的写的非常多,比如。不是说写的多不好,只是有些场景不需要写测试用例反倒写了 ,比如一个函数只是一个简单的变量自增操作,如果类似这样的函数都写上测试用例,会花费开发的过多时间和精力,反而得不偿失。同时也会大大增加代码量,造成逻辑混乱。因此如何拿捏好哪些需要些测试用例哪些不需要写,也是一门艺术。例如:暴漏在.h中的方法需要写测试用例, 而那些私有方法写测试用例的优先级要低的多了。
  对于测试用例覆盖度多少合适这个话题,也是仁者见仁智者见智,其实一个软件覆盖度在50%以上可以称为一个健壮的软件了,要达到70,80这些已经是非常难了,不过我们常见的一些第三方开源框架的测试用例覆盖率还是非常高的,让人咋舌。例如,AFNNetWorking的覆盖率高达87%,SDWebImage的覆盖率高达77%。

  AFN测试用例覆盖率87%

  SD测试用例覆盖率77%

  四.依赖注入
  依赖注入是一种分离依赖,减少耦合的技术。可以帮助写出可维护,可测试的代码。那么为什么这里要提到依赖注入呢?因为在我们的单元测试中,某些时候模块间的依赖太高,会影响我们的单元测试,合理使用依赖注入帮助我们进行测试。
  先举一个:
#import "Teacher.h"
#import "Student.h"
@implementation Teacher
- (void)guideStudentRead
{
Student * gaoStu = [[Student alloc] initWithName:@"MrGao"];
[std read];
}
@end
  上面这段代码是在Teacher类中一个guideStudentRead(指导学生读书)的方法中创建一个名字叫MrGao的学生,并且这个gaoStu开始读书。这个时候其实是有一个强依赖关系的,Teacher对Student有一个依赖。这种有强依赖关系的代码其实非常不利于单元测试,首先Teacher在该方法中需要去操作一个不受自己控制的Student对象,并且如果后期修改扩展,MrGao改为了MrHu,那这个guideStudentRead方法内部也要做相应的修改。那么我们如何一个依赖对象的方法进行修改方便单元测试呢?我们可以做出如下修改:
#import "Teacher.h"
#import "Student.h"
@interface  Teacher ()
@property (nonatomic, strong) Student * stu;
@end
@implementation Teacher
- (instancetype)initWithStudent:(Student *)student
{
if (self = [super init]) {
self.stu = student;
}
return self;
}
- (void)guideStudentRead
{
}
@end
  我们现在可以通过一个属性来存储依赖对象,在Teacher的init方法中传过来一个Student对象,这个Student对象的初始化不适在Teacher类中,而是在外部已经创建好注入到Teacher类中,从而Teacher和让Student产生依赖,这个是我们要讲的依赖注入。这样不仅减轻了依赖,也提高代码的可测试性。注入方法有很多种,我们这里使用init注入的方法称之为构造器注入。还有其他属性注入,方法注入,环境上下文注入和抽取和重写调用注入。在OC中还有一些的依赖注入开源框架,比如Objection 和Typhoon 。关于依赖注入这里简单介绍一下,如果大家还有兴趣可以继续查阅相关依赖注入的相关文档。