一、前言
由于苹果开发的封闭性,一直以来都没有良好的测试驱动开发支持。怀抱在iOS开发中实现TDD持续集成的工程师们为此提供了许多三方库以达到与Android开发同等的TDD支持,然而由于其学习成本高,普及性始终不够广泛,致使大部分iOS开发工程师形成了APP开发无需单元测试或UI自动化测试的错误认识。
直至XCTest替代OCUnit,并提供了异步测试、性能测试等新特性,单元测试的使用者越来越多。XCode7提供了UI Testing以替代Instrument中的Automation测试,真正使TDD在iOS开发中走上了康庄大道。
Unit Test中可以直接访问目标Target的代码,因此对代码覆盖率的支持一直是良好的。
然而如何在UI Testing中支持代码覆盖率,以及增强代码覆盖率的可读性,在APP中获得接入SDK的代码覆盖率等问题,笔者在Forum.developer.com、StackOverflow、Google等各方搜索,均只看到提问,而没有答案。一方面固然是iOS开发者对TDD的不重视、不习惯,另一方面也是由于苹果官方对这些问题没有明确的答案。
经过摸索与验证,特别将解决方案分享给有需要的朋友们。
二、UI Testing的单元测试编写方法
之前已经有文章讲述过,再此不做重述,提供传送门
1. 国外的一篇文章 UI Testing in Xcode 7, Part 1: UI Testing Gotchas
2. 猫王的 WWDC15 Session笔记 - Xcode 7 UI 测试初窥
三、解决前言中提到的几个问题
1. UI Testing 中代码覆盖率的支持
查看Xcode各版本发布纪要
Known Issues in Xcode 7 beta — IDE
Xcode does not show code coverage information for source files in static libraries. (15605406)
Workaround: Add the source files directly to application or framework targets.
Code coverage does not work with UI testing. (20966994)
Resolved in Xcode 7 beta 4 - IDE
Code coverage works with UI testing(20966994)
Resolved in Xcode 7 beta 5 - IDE
Code Coverage now supports files in static libraries. The source files show up under each binary that links the static library in the Coverage tab of the Test Report. The source editor shows coverage numbers aggregated across all binaries.(21984681)
从发布纪要可以得知,目前Xcode中UI Testing对代码覆盖率的兼容,以及获取静态库的代码覆盖率都有明确的支持。然而,在Xcode7.0-7.2的尝试中,UI Testing始终得不到代码覆盖率,才深度研究了如下的解决方法。!!!然而在最新的Xcode 7.3beta中,发现不添加如下代码也能够看到UI Testing的代码覆盖率了!!!,白费了那么久的捣腾啊。不过,如果你的XCode未能在UI Testing 中给出代码覆盖率。尝试在main.m中加入如下代码:
1 #import <UIKit/UIKit.h> 2 #import "AppDelegate.h" 3 4 dispatch_source_t SIGTERMHandlerSource; 5 6 int main(int argc, char * argv[]) { 7 @autoreleasepool { 8 9 signal(SIGTERM, SIG_IGN); 10 11 SIGTERMHandlerSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGTERM, 0, dispatch_get_main_queue()); 12 13 dispatch_source_set_event_handler(SIGTERMHandlerSource, ^{ 14 exit(0); 15 }); 16 dispatch_resume(SIGTERMHandlerSource); 17 18 19 return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 20 } 21 }
这里代码的主要意义是使系统不去处理 SIGTERM 信号,而自行提供处理方法。修改main.m文件之后,在跑UI自动化测试时就可以看到代码覆盖率了!
Code Coverage的基本设置图如下,更改Scheme的Test中的勾选项,把 Gather coverage data 勾上,然后就可以Command+U或者指定某个UI自动化用例执行了。另外,在Scheme的编辑中,可以指定Test时需要测试哪些单元测试,具体见图
2.如何获得静态包的代码覆盖率?
接入APP是不能直接访问静态库工程的文件的,因此接入APP的UI测试得不到静态库工程的代码覆盖率,如何解决呢?很简单,虽然静态包不能单独运行UI,但可以在静态库的Scheme-Test中添加接入APP的测试Target,就可以在静态库的project里用看到UI Testing的用例并执行。测试类也可以直接访问到静态库工程中的文件,并得到代码覆盖率结果了。同样,这里也需要勾选上 Gather coverage data, 如下图:
再看我们得到的代码覆盖率结果,鼠标悬浮在蓝条时会有数值,点击箭头能进到文件看方法的执行次数。如图
结果是得到了,但每次都要悬浮到蓝条上看数值,以及得不到整体统计,可读性非常差。
3.如何使代码覆盖率测试结果增强可读性?
测试结果文件的位置,从工程目录的生成app,打开finder,往上找Intermediates文件夹就行了。Coverage.profdata就是我们要找的结果文件。
XCode代码测试结果使用了LLVM Code Coverage Mapping Format, 具体格式介绍参考http://llvm.org/docs/CoverageMappingFormat.html。只要是标准格式,就可以按想要的方式解读。这时llvm-cov上场了,llvm是随Xcode一起安装的,不需要另外安装。如果遇到解析文件报错LLVM版本不对的,可以用Xcode-select
--print-path看下生成文件时的Xcode路径和跑用例时用的Xcode是不是一个,如果不是,重新选择。
解析文件的命令为xxx/llvmoconv report -instr-profile xxx/Coverage.profdata xxx/xx.app/xx,其中report为参数,可以换成show得到各文件结果。xx.app要取与Coverage.profdata同目录的Products文件夹内的。
这时,我们可以直观的得到各个文件的代码覆盖率数值信息,这里带入了杂质信息,真机和模拟器均存在。但是,这是无关紧要的。我们APP开发或SDK开发都会习惯添加类前缀,那么通过使用shell脚本筛选我们需要的行,重新计算统计值,就能得到app部分文件或静态库文件的统计结果。