iOS 单元测试--逻辑测试

作者: 爱自由鹏 | 来源:发表于2016-05-23 17:39 被阅读1387次

    单元测试分为3种:

    • 逻辑测试:测试逻辑方法
    • 异步测试:测试耗时方法(用来测试包含多线程的方法)
    • 性能测试:测试某一方法运行所消耗的时间

    本节介绍逻辑测试,直接创建一个项目,并选中Include Unit Tests选项。


    创建工程时选中Unit Tests.png

    1.新建一个Person类,提供字典转模型的方法,这个方法就是需要进行单元测试的:

    + (instancetype)personWithDict:(NSDictionary *)dict;//字典转模型方法

    字典转模型方法的单元测试.png

    原始的Person.m文件:

    #import "Person.h"
    @implementation Person
    + (instancetype)personWithDict:(NSDictionary *)dict {
        Person *obj = [[self alloc] init];
        [obj setValuesForKeysWithDictionary:dict];
        return obj;
    }
    @end
    

    2.下面,我们打开单元测试Tests黄色虚拟文件夹,然后Command+N 创建一个单元测试文件。文件命名格式:类名+Tests,继承自:XCTestCase (如下图示例)

    新建一个Unit Test文件.png 单元测试文件的命名格式.png

    创建完毕,可以看到Tests文件里有如下4个方法:

    系统自带UnitTest文件案例.png
    单元测试开始前,先了解几个知识点:
    • 单元测试是以代码测试代码
    • 红灯/绿灯迭代测试开发
    • 自己建立测试用例(使用例子数据,专门检查边界点)
    • 单元测试不是靠 NSLog 来测试,NSLog 是程序员用眼睛看的笨办法。
      使用 断言 来测试的,提前预判条件必须满足!
      XCTAssert(expression, ...)
      XCTAssert(条件, 不满足条件的描述)

    3.准备工作已完成,下面正式开始单元测试:
    导入Person.h头文件,仿照文件自带的 - (void)testExample {} 格式,写这样一个逻辑测试方法:- (void)testNewPerson {} ,然后Command+S保存,左边会出现菱形白色方框,它就是执行单元调试的按钮🔘,在调试过程中,我们需要反复点击这个按钮去调试。方法里书写测试代码。

    PersonTests.m文件:

    // 逻辑测试方法
    - (void)testNewPerson {
    
        // 1.测试 name和age 是否一致
        [self checkPersonWithDict:@{@"name":@"zhou", @"age":@30}];
        
        /** 2.测试出 age 不符合实际,那么需要在字典转模型方法中对age加以判断:
         if (obj.age <= 0 || obj.age >= 130) {
         obj.age = 0;
         }
         */
        [self checkPersonWithDict:@{@"name":@"zhang",@"age":@200}];
        
        // 3.测试出 name 为nil的情况,因此在XCTAssert里添加条件:“person.name == nil“
        [self checkPersonWithDict:@{}];
        
        // 4.测试出 Person类中没有 title 这个key,在字典转模型方法中实现:- (void)setValue:(id)value forUndefinedKey:(NSString *)key {}
        [self checkPersonWithDict:@{@"name":@"zhou", @"age":@30, @"title":@"boss"}];
        
        // 5.总体再验证一遍,结果Build Succeeded,测试全部通过
        [self checkPersonWithDict:@{@"name":@"zhou", @"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, @"年龄超限");
        }
    }
    

    最好调试修改后的Person.m文件:

    #import "Person.h"
    
    @implementation Person
    
    + (instancetype)personWithDict:(NSDictionary *)dict {
        Person *obj = [[self alloc] init];
        
        [obj setValuesForKeysWithDictionary:dict];
        
        // 预防处理age超限
        if (obj.age <= 0 || obj.age >= 130) {
            obj.age = 0;
        }
        
        return obj;
    }
    
    // 预防处理没有找到的key
    - (void)setValue:(id)value forUndefinedKey:(NSString *)key {}
    
    @end
    

    注意事项:

    • 不是所有的方法都需要测试。
      例如:私有方法不需要测试!只有暴露在 .h 中的方法需要测试!面向对象有一个原则:开闭原则!
    • 所有跟 UI 有关的都不需要测试,也不好测试。
      业务逻辑 代码封装出来!变成可以测试的代码,让程序更加健壮!
    • 一般而言,代码的覆盖度大概在 50% ~ 70%
      从github上得知:YYModel测试覆盖度为83%,AFNetworking测试覆盖度为77%,两者都是比较高的。

    相关文章

      网友评论

      • 芷依儿:项目需要第一次需要些单元测试,不是很明白啊,项目那么多方法,要一个一个都写在这个testExample方法里面吗?其他类的方法都导入头文件写在这一个方法里吗?这样感觉有点复杂。。。
      • 程序小胖:写的实在是专业,看着特别舒服
        爱自由鹏:谢谢!
      • 帅气影中人:大神问个问题:单元测试中能带入数据库么
        爱自由鹏:我没试过,你可以试试
      • 戒惜舍得:把单元测试写得这么专业的人目前只见过一位。想作者致敬!
        爱自由鹏:@戒惜舍得 谢谢这个高的评价,一起在追梦探索的路上加油!

      本文标题:iOS 单元测试--逻辑测试

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