概述
  本文为多篇iOS单元测试文章总述和实践总结。前部分分析iOS开发单元测试的目的和原则,后部分讲述Kiwi和OHHTTPStubs框架的基本用法及问题记录。
  单元测试目的:
  使重构更简单——重构后快速通过单元测试回归旧有功能
  避免代码恶化——设计API考虑更全面,对提高设计和可扩展性有帮助
  提供可执行的说明和文档
  降低开发软件的代价——更快速地编译和修改代码,降低开发时间和风险
  测试对象:
  与UIView无关的类,例如:ViewModel层和工具类
  待测试类的所有Public API均需要测试
  不应该测试什么:
  不测私有方法,私有方法数量多,修改不需要专门通知
  避免stub私有方法(理由同上,且私有方法如果已被修改或删除,仍然对其按旧逻辑stub单元测试不再精确)
  避免stub外部库,第三方代码不应该直接在测试中出现,因为如果出现第三方库这个替换的情况,则单元测试也均需要修改(也说明了分层设计、对第三方库进一步封装的重要性)
  可不考虑ViewController和View的测试,因为改动频繁,维护测试代码代价高,对象创建依赖xib、Storyboard也不方便
  测试需要具备的特性:
  执行快速、可自动执行
  能隔离——不依赖外部因素或其它测试结果,要可以假设其它需要依赖的类和方法均已通过单元测试
  可重复——每次运行都应该产生相同的结果
  自带检测——包含断言,不需要人为干预即显示测试结果
  需要后期维护——公开API的逻辑修改、接口增减,均需要对相应执行失败的测试用例进行更新
  使用框架
  XCTest测试框架
  与Xcode直接集成,项目默认已添加,使用方便,测试结果展示直观。
  测试用例被分到继承XCTestCase的不同子类中去。每个以test为开头的方法都是一个测试用例。
  缺点:
  书写性和读写性不好
  测试用例过多时,各个测试方法是割裂的,在测试文件中找到某个特定的测试和弄明白这个测试在做什么不容易
  测试都是由断言完成的,很多时候断言的意义不是特别明确
  测试的描述都是加在断言之后的,夹杂在代码中,难以寻找阅读
  难以mock或stub
  Kiwi测试框架
  iOS平台经典的行为驱动开发(BDD)测试框架,基于XCTest封装,解决了上述XCTest的缺点。
  Kiwi提倡通过将测试语句转换成类似自然语言的描述,开发人员可以使用更符合大众语言的习惯来书写测试,在项目交接或者修改的时候更加顺利。
  详细用法可参考:https://github.com/kiwi-bdd/Kiwi/wiki
  测试文件
  一个测试文件所包含一组对于类行为的描述,习惯上使用需要测试的目标类来作为名字,并以Spec作为文件名后缀,例如:XXViewModelSpec.m为测试XXViewModel类的测试文件。
  测试文件的创建:
  安装Xcode Kiwi模板:下载模板文件Xcode Templates,运行install-templates.sh安装;
  按路径Xcode-New-File-Kiwi Spec创建
  编辑好测试代码后,Cmd+U快捷键可运行单元测试,左侧导航栏的Test navigator会展示测试结果
  Kiwi文件结构和简单用法
// XXViewModelSpec.m文件内容
SPEC_BEGIN(XXViewModelSpec) //括号内为测试文件名
// describe即Give,第一个参数为要测试的类名
// 1个测试文件建议只有一个describe
describe(@"XXViewModel", ^{
        // context即When,第一个参数描述测试的条件,block内为测试的具体内容
        // 1个describe内可包含多个context
    context(@"when created", ^{
        __block XXViewModel *viewModel = nil;
       
        // 每个测试用例均在执行前均会执行beforeEachblock内的代码
        beforeEach(^{
            viewModel = [[XXViewModel alloc] init];
        });
       
        // 每个测试用例均在执行后均会执行afterEach内的代码
        afterEach(^{
            viewModel = nil;
        });
       
        // it的block内容即为测试用例
        // it即Then,第一个参数描述测试的期望结果,在block内对代码执行情况进行验证
        it(@"should not be nil", ^{
                // shouldNot、beNil均为kiwi的方法宏,
                // 测试期望的检测一般采用:[[testObje should] doSomething] 或[[testObje shouldNot] doSomething] 的形式,
                // should和doSomething部分的可填内容参考:https://github.com/kiwi-bdd/Kiwi/wiki/Expectations
            [[viewModel shouldNot] beNil];
        });
       
        // 1个context内可包含多个it,即多条测试用例
        it(@"should have 2 sections", ^{
                    // theValue时kiwi的语法糖,负责将简单数据类型转换为NSNumber
            [[theValue([viewModel numberOfSections]) should] equal:@(2)];
        });       
    });
}; 
SPEC_END
  BDD测试格式:
  BDD测试用例的描述一般采用:Given..When..Then格式。
  依次表示:
  Give:测试用例的测试说明。
  When:具体测试条件。
  Then:期望的测试结果。
  比如,上述单元测试用例,可以用自然语言描述为:
  Given a XXViewModel, when created, it should not be nil.
  Given a XXViewModel, when created, it should have 2 sections
  这样测试用例表意明确,具有了文档性,出错时方便定位修改。