WWDC之Testing in Xcode6

screenshot.png

前言

软件工程中测试一直是很重要的组成部分,影响着软件的质量和开发的效率.作为App开发也毫不例外,虽然在移动设备上的代码测试在国内还没有大范围普及,但许多大型公司或者优秀的创业公司都对代码测试有很高的要求和看重,最近也在想如何去减少App开发中带来的各种Bug和如何去更好地提高App质量,心里第一个想法就是写测试,用测试来说明代码的执行结果,用测试来验证代码的功能.因此最近开始了解相关iOS单元测试方面的内容,学习如何在iOS开发中写单元测试.该Session虽然是关于Testting in Xcode6,但所涉及的内容在Xcode7都没有太多变化, Xcode7主要是增加了UI Test和查看测试覆盖率的功能, 所以该Session对了解如何在现在Xcode写单元测试还是很有参考价值.

内容

一. 为什么要测试

  • 发现Bug

帮助开发者尽早地尽多地发现程序中的Bug,并且提前去修复它,而不是将程序发布给客户后,由客户反馈各样的Bug,再去进行修复.

  • 扩展

当对程序进行完善或者新功能的开发时,通过添加新的测试用例或者原来测试用例代码来检查新加入的代码对原程序是否所有影响.

  • 维护

程序具有完整的测试用例和测试内容让其他开发者能更加容易地交接,使更快地熟悉程序中API的行为和功能.

二. 测试的工作流

主流的测试流程有两种:
第一种,行为驱动开发(又称BDD)的测试

  1. 代码实现功能
  2. 为其行为添加测试
  3. 验证代码通过测试

第二种, 测试驱动开发(又称TDD)的测试�

  1. 写无法通过的测试
  2. 写能通过测试的代码
  3. 重构通过测试的代码去实现功能

测试通过就会测试用例的代码呈现绿色状态,而测试失败就会呈现红色状态


Test State

三.如何在Xcode进行测试

使用XCTest

XCTest Framework是Xcode的测试框架,在Xcode5发布时推出.它提供一个类XCTestCase作为开发者自己实现测试的基类, 在继承此类的基础上实现自己的测试方法, 方法的命名要符合以test开头的规范,尽量让测试方法的名字表达测试的功能和期望的行为,例如testDocumentOpening; 提供了assertion API比如XCTAssertEqual, XCTAssertNotNil...来帮助判断测试的失败还是成功.

使用 Test Target

Test Target

Xcode中独立的Test Target进行对测试用例代码的管理, 类似与一般程序的Target, Test Target进行自己的包资源和测试代码的管理,每个Target都是独立,互不干扰,新建工程时,Xcode都会默认了配置一个Unit Test Target,(Xcode 7后有多了一个UI Test Target),也允许在原有的工程上直接添加一个或者多个Test Target.

Xcode测试时的注意点

  • 针对测试的内容,需要手动地import,让测试进行注入你的App.
  • 存在于Test Target的文件,图片资源,并不在main bundle中,要使用[NSBundle bundleForClass:[MyTest class]]来资源访问.

如何运行测试用例
针对已经写好的测试用例,运行测试用例的四种方式

  1. 快捷键 Cmd + U,进行当前scheme的所有测试Target的测试
  2. 点击测试用例方法的侧边栏的按钮,运行当前测试Target用例方法的测试
  3. 点击Xcode左边导航栏的Test Navigator的按钮,进行当前scheme的所有测试用例的测试
  4. 使用命令行 xcodebuild运行指定scheme的测试用例
    xcodebuild test
    -projetc ~/Document/MyApp.xcodeproj
    -scheme MyApp
    -destination platform=iOS,name=iPhone

异步代码测试

开发App时往往为了程序的效率使用多线程,对存在耗时的,不必要阻塞主线程的代码进行异步执行,比如代理和Blcok的异步回调, 网络请求, 后台数据处理等操作代码, 使得对异步代码的测试也显得比较特殊,XCTest框架有异步代码的测试也有很好的API支持.

// 1
- (XCTestExpectation *)expectationWithDescription:(NSString *)description; 
- (void)waitForExpectationsWithTimeout:(NSTimeInterval)timeout handler:(nullable XCWaitCompletionHandler)handler;

// 2
- (XCTestExpectation *)expectationForNotification:(NSString *)notificationName object:(nullable id)objectToObserve handler:(nullable XCNotificationExpectationHandler)handler;
  1. 通过根据测试行为的描述自定义一个XCTestExpectation对象, 然后设置异步测试的超时时间以及处理回调Handler,在进行异步代码执行测试时,进行常规的测试断言,然后使用XCTestExpectation类的fulfill方法来响应之前定义的XCTestExpectation对象,表示异步测试的结束,具体执行顺序如下示例代码:

     XCTestExpectation *expectation = [self expectationWithDescription:"open doc"];
     UIDocument *doc = ...;
     [doc openWithCompletionHandler:^(BOOL success) {
         XCTAssert(success);
         [expectation fulfill];
     }];
     
     [self waitForExpectationsWithTimeout:5.0 handler:nil];
    
  2. 使用通知的方式,先利用expectationForNotification:进行异步测试时间的通知注册,然后设置超时时间,然后进行异步代码执行的过程中,使用NSNotificationCenterpostNotificationName:方法主动去响应之前注册的通知,具体执行顺序如下示例代码:

     UIDocument *doc = ...;
     [doc openWithCompletionHandler:^(BOOL success) {
         XCTAssert(success);
         [[NSNotificationCenter defaultCenter] postNotificationName:@"OpenDoc" object:nil];    
    

    }];

     [self expectationForNotification:@"OpenDoc" object:nil handler:nil];  // 提示:设置Notification Name一定要统一,否则测试会失败
     [self waitForExpectationsWithTimeout:5.0 handler:nil];
    

单元测试中的性能测试

Xcode为了提供性能的单元测试专门提供了UI和新API,来检测测试代码的执行时间和性能波动情况;使用- (void)measureBlock:(void (^)(void))block;在Block中进行性能测试代码的调用,里面的代码会被运行10次,并且记录平均时间和十次运行的性能波动情况.

存在性能测试失败的测试用例,就可以通过Profile的TimeProfile工具进行具体性能问题代码的定位: 在Xcode左边导航栏的Test Navigator的右击需要Profile的测试用例,选择Profile testMethod.

对每一段代码性能执行测试后Xcode会提供Baseline界面, 呈现上一次测试运行的性能耗时分布,第一次运行后需要设置Baseline作为之后性能测试的标准,在之后的测试过程中,如果新的性能平均分布增加了10%,XCTest就会认定此次性能测试的失败;

Note: Baseline的信息保存在设备中,同样的测试代码,针对真机性能代码测试的Baseline不会在模拟器中测试时使用.

对于性能代码的测试, Xcode还提供了性能测试结果的标准偏差(STDDEV),若果偏差超过上次偏差平均值的10%,(可手动调整)就会被认定测试失败, 而当偏差不到0.1秒就会被XCTest忽略, Session也说明影响STDDEV的因素主要有: 针对文件或者网络的IO操作; 每一次回调时进行不同的工作; 系统处理进程时的忙碌,这些都可能使得STDDEV变高.

为了能更真实地进行代码的性能测试,Session建议只对需要进行性能测试的代码进行测试,其他关联性小的代码进行不要去影响测试结果,提供了一种更好的使用measureBlock的方式,如下实例代码

       [self measureMetrics:@[XCTPerformanceMetric_WallClockTime] automaticallyStartMeasuring:NO forBlock:^{
        
        for (NSInteger index = 1; index < 10000; index++) {
            
            [self startMeasuring]; 
            NSString *string = [NSString stringWithFormat:@"%ld==", index];
            [self stopMeasuring];
            // 只在需要测试的地方使用start和stop Mesure方法;
            [array addObject:string];
        }
    }];

总结

虽然软件工程中没有真正的"银弹",测试也不是万能的,不会让程序开发的Bug多一一消除,但保持良好的软件编程习惯,学会为自己的代码写测试,尽可能保证代码的正确和简洁,不论是App开发还是其他领域的开发,对于开发人员都是一种编程能力的提现,以后尝试多写写测试,可以从网络层或者数据层开始尝试,更加容易些.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容