单元测试详解

作者: 江水东流 | 来源:发表于2016-10-24 15:46 被阅读100次

    只有优秀的程序员才关心产品,只有优秀的程序员才在乎程序的性能,我们要具备性能测试和性能优化的能力,今天开始我开始给大家写一写介绍性能方面的文章,今天写单元测试.

    单元测试简介如下:

    1. 单元测试是以代码测试代码
    2. 红灯/绿灯迭代开发
    3. 在日常开发中,数据大部分来自于网络,很难出现所有的边界数据!如果没有测试所有条件就上架
      在运行时造成闪退!
    4. 自己建立测试用例(使用的例子数据,专门检查边界点)
    5. 单元测试不是靠 NSLog 来测试,NSLog 是程序员用眼睛看的笨办法。

    提示:

    1. 不是所有的方法都需要测试
      例如:私有方法不需要测试!只有暴露在 .h 中的方法需要测试!面向对象有一个原则:开闭原则!
    2. 所有跟 UI 有关的都不需要测试,也不好测试!
      MVVM,把 小的业务逻辑 代码封装出来!变成可以测试的代码,让程序更加健壮!
    3. 一般而言,代码的覆盖度大概在 50% ~ 70%

    demo github地址 https://github.com/1271284056/Unit-test-Demo
    希望大家多给点星星~~

    1. 创建项目时候勾选下面的Include Unit Tests


      屏幕快照 2016-10-24 10.08.08.png
    2. 如果创建项目时候没勾选这个选项,通过下图操作为项目添加单元测试.

    1448355504907178.png 1448355510545176.png
    1. 在单元测试文件夹新建测试类
    屏幕快照 2016-10-24 11.53.40.png
    1. 首先建立测试类Person类,建立它的初始化方法,以及异步方法.

    Preson.h

    import <Foundation/Foundation.h>

    @interface Person : NSObject
    @property (nonatomic, copy) NSString *name;
    @property (nonatomic) NSInteger age;

    • (instancetype)personWithDict:(NSDictionary *)dict;
      /// 异步加载个人记录
    • (void)loadPersonAsync:(void (^)(Person *person))completion;
      @end

    Preson.m

    import "Person.h"

    @implementation Person

    • (instancetype)personWithDict:(NSDictionary *)dict {
      Person *obj = [[self alloc] init];
      [obj setValuesForKeysWithDictionary:dict];
      if (obj.age <= 0 || obj.age >= 130) {
      obj.age = 0;
      }
      return obj;
      }
    • (void)setValue:(id)value forUndefinedKey:(NSString *)key {}
    • (void)loadPersonAsync:(void (^)(Person *))completion {
      dispatch_async(dispatch_get_global_queue(0, 0), ^{
      [NSThread sleepForTimeInterval:1.0];
      Person *person = [Person personWithDict:@{@"name": @"z", @"age": @5}];
      dispatch_async(dispatch_get_main_queue(), ^{
      if (completion != nil) {
      completion(person);
      }
      });
      });
      }

    单元测试方法名都以test开头,写完后左边会有一个菱形,点击菱形,运行变成绿色就是成功,红色表示失败.

    • (void)testNewPerson {
      [self checkPersonWithDict:@{@"name": @"zhang", @"age": @20}];
      [self checkPersonWithDict:@{@"name": @"zhang"}];
      [self checkPersonWithDict:@{}];
      [self checkPersonWithDict:@{@"name": @"zhang", @"age": @20, @"title": @"boss"}];
      [self checkPersonWithDict:@{@"name": @"zhang", @"age": @200, @"title": @"boss"}];
      [self checkPersonWithDict:@{@"name": @"zhang", @"age": @-1, @"title": @"boss"}];
      // 到目前为止 Person 的 工厂方法测试完成!
      }
      /// 根据字典检查新建的 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, @"年龄超限");
      }
      }

    异步单元测试

    /**
    苹果的单元测试是串行的
    setUp
    testXXX1
    testXXX2
    testXXX3
    tearDown
    中间不会等待异步的回调完成
    */

    /// 测试异步加载 Person

    • (void)testLoadPersonAsync {
      // Xcode 6.0 开始解决 Expectation 预期
      XCTestExpectation *expectation = [self expectationWithDescription:@"异步加载 Person"];
      [Person loadPersonAsync:^(Person *person) {
      NSLog(@"%@", person.name);
      // 标注预期达成
      [expectation fulfill];
      }];
      // 等待 10s 期望预期达成 10秒后如果执行到[expectation fulfill]这里表面单元测试成功
      [self waitForExpectationsWithTimeout:10.0 handler:nil];
      }

    性能测试

    以前我们测试一个函数执行时间,在开始时候定义起始时间
    NSTimeInterval start = CACurrentMediaTime();
    函数执行完毕后计算时间
    NSLog(@"%f", CACurrentMediaTime() - start);
    单元测试为我们提供一个- (void)testPerformanceExample {}函数,在这个函数的 [self measureBlock:^{}] 中写耗时操作, 相同的代码重复执行 10 次,统计计算时间,平均时间在控制台打印出来!
    // Performance 性能!

    /*
    性能测试代码一旦写好,可以随时测试!
    */

    • (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 < 10000; i++) {
      [Person personWithDict:@{@"name": @"zhang", @"age": @20}];
      }
      }];
      }

    相关文章

      网友评论

        本文标题:单元测试详解

        本文链接:https://www.haomeiwen.com/subject/moafuttx.html