写点什么

iOS 开发中的单元测试(一)

  • 2013-06-05
  • 本文字数:3412 字

    阅读完需:约 11 分钟

导读:本文不讨论单元测试是什么,或者它之于一个工程的利弊,我认为单元测试是一个开发者保证产出代码质量的有效工具。本文从使用者的角度对比当下比较流行的两款单元测试框架,给大家提供一些选用建议。如果你还不甚了解单元测试在工程中所起到的作用,或者还不知道TDD的开发模式,可参考: Test-Driven Development Unit Testing

本文对比两个 iOS 开发中常见的单元测试框架:OCUnit,被官方集成进 XCode 4.x 版本中;GHUnit,被推荐最多的测试框架,带 GUI 界面。初窥两款测试框架非常相似,而上手使用就会发现其中的区别。细节上的区别使两款框架在不同角度各有优劣。

OCUnit

OCUnit 是 XCode 4.x 集成的单元测试框架,OCUnit 中的测试分为两类,一类称为 Logic Tests,另一类称为 Application Tests。Logic Tests 更倾向于所谓的白盒测试,用于测试工程中较细节的逻辑;Application Tests 更倾向于黑盒测试,或接口测试,用于测试直接与用户交互的接口。

• 添加单元测试

OCUnit 是 XCode 集成的,所以其与工程的结合理应是最好的,添加到工程中的成本也理应最低。使用 XCode 创建新工程的流程中就有一个“Include Unit Tests”的选项(如图 1),新的工程就会自动生成一个 Logic Tests。

向已存在的工程中添加 OCUnit Logic Tests 也不复杂,只需要添加一个类型为:“Cocoa Touch Unit Testing Bundle”的 Target 即可(如图 2)。

图 2,向已存在的工程中添加 OCUnit 测试

向已有工程中添加一个测试 Target 时,XCode 会自动生成一个 Scheme,运行单元测试用例和 Build 原工程需要切换不同的 Scheme。如果认为切换 Scheme 非常麻烦,也可以在添加 Target 之前,在“Manage Scheme”菜单中取消“Autocreate schemes”(如图 3)。

图 3,添加 Target 不创建 Scheme

Application Tests 要基于 Logic Tests 做一些修改。一般来说一个工程既需要 Logic Tests 也需要 Application Tests,所以建议按照上述方法添加一个单独的 Target,然后执行以下操作(如图 4):

1. 在 Build Settings 中搜索“bundle loader”,设置为:$(BUILT_PRODUCTS_DIR)/APP_NAME.app/APP_NAME(APP_NAME 是应用名)

2. 再搜索“test host”,设置为:$(BUNDLE_LOADER)

3. 在 Build Phases-Target Dependencies 中添加依赖,选择主程序 Target

图 4,添加一个 Application Tests

• 创建测试用例

OCUnit 的测试用例最常用的方法有三个

1. - (void)setUp:每个 test 方法执行前调用

2. - (void)tearDown:每个 test 方法执行后调用

3. - (void)testXXX:命名为 XXX 的测试方法

添加 Target 之时 XCode 已经自动创建了一个测试用例类:UnitTestDemoTests,其中 UnitTestDemo 是工程的名字,该类中已经包含了 setUp,tearDown 和 testExample 三个方法。

通过 command+n,选择“Objective-C test case class”创建一个新的测试用例类(如图 5)。通过 XCode 创建的测试用例类是一个继承自 SenTestCase(OCUnit 由 SEN:TE 公司开发,因此基类命名为 SenTestCase)的空类,需要模仿 UnitTestDemoTests 编写测试方法。

图 5,创建一个测试用例类

开发者可以自己实现无返回值,且命名规则为 testXXX 的实例方法,并使用框架提供的大量断言方法。

Logic Tests 与 Application Tests 的区别主要在 setUp 方法,Logic Tests 只需在 setUp 方法中初始化一些测试数据,而 Application Tests 需要在 setUp 方法中获取主应用的 AppDelegate,供 test 方法调用。

值得注意的是,OCUnit 的 test bundle 是侵入主应用的,因此在使用过程中要十分注意,不要让单元测试的资源覆盖主应用资源,造成诡异的 Bug。

• 运行测试

由于 OCUnit 是集成在 XCode 中的框架,因此在 XCode 中运行也比较方便。切换到单元测试的 scheme(如果与工程共用 scheme 则无需切换),Product->Test(或直接使用快捷键 command+u),框架会自动查找所有工程中 SenTestCase 的子类,运行其中全部命名类似 testXXX 的无返回值方法。

• 测试反馈

OCUnit 的失败方法会通过 Console 和 XCode Issues 两个位置反馈,通过 XCode Issues 可以直接定位到出现错误的单元测试代码行。Issue 的提示信息就是在单元测试断言方法中定义的 description。

GHUnit

GHUnit 是一款 Objective-C 的测试框架,除了支持 iOS 工程还支持 OSX 的工程,但 OSX 不在本文的讨论范围。GHUnit 不同于 OCUnit,它提供了 GUI 界面来操作测试用例,而且也不区分 Logic Tests 和 Application Tests。

• 添加单元测试

与集成进 XCode 的 OCUnit 相比,GHUnit 的添加过程略显复杂。首先在上下载 GHUnit 的框架包,当前的 For iOS 的最新版本是 0.5.6,解压后是一个 GHUnitIOS.framework 的文件夹。

打开已经存在的工程,添加一个 EmptyApplication Target,并在新 Target 中添加刚刚下载的 GHUnitIOS.framework(如图 6、7)。

图 6,在新 Target 中添加 GHUnitIOS.framework

在 Build Phases 中添加非官方框架并不会把框架文件拷贝到工程目录,而是只做一个链接,所以建议在添加之前先把框架拷贝到工程目录下。

图 __7_,选择 __GHUnitIOS.framework_

接下来用相同的方法添加框架依赖的其他库:“QuartzCore.framework”。

在 Build Settings 中搜索“linker flags”,设置 Other Linker Flags - Debug - 添加一个支持全架构和全版本 SDK 的标示“-ObjC -all_load”(如图 8)。

图 __8_,设置 __linker flags_

删除 Tests Target 中的 AppDelegate(.h 和.m 一起删除)。修改 main 函数,支持 GHUnitIOS,导入 GHUnitIOSAppDelegate 代替原来的 AppDelegate,修改 UIApplicationMain 的参数(如图 9)。

图 __9_,修改 __main__ 函数 _

至此已经完成了 GHUnit 的添加,选择新建 Target 同时创建的 scheme,直接 Build and Run 即可在设备或 Simulator 中启动一个新的 App(如图 10),即该单元测试的 App。

图 __10_,单元测试 __App_

• 创建测试用例

创建 GHUnit 测试用例与创建 OCUnit 测试用例相似。

新建一个 Objective-C Class 文件,继承自 GHTestCase,在 XCode 生成的.h 文件中不会导入 GHUnit.h 文件,需要开发者自行导入“#import <GHUnitIOS/GHUnit.h>”。

GHUnit 框架提供断言方法比 OCUnit 更加丰富,开发用例也就可以做的更加细致,更有利查找 / 定位错误。

测试方法的命名规则与 OCUnit 一样,是以 test 开头的无返回值方法:- (void)testXXX。而常用的方法除了上述提到的 setUp 和 tearDown,GHUnit 还提供了 setUpClass 和 tearDownClass 两个方法,在该用例运行前和结束后调用。另外,刚刚提到 GHUnit 不区分 Logic Tests 和 Application Tests,所以在 setUp 和 tearDown 方法中也就不存在设置的区分。

• 运行测试

运行 GHUnit 需要分两步,首先编译并安装单元测试 App 到设备或 Simulator 里(如图 11),创建了两个用例,每个用例中分别有一个方法。

图 __11_,两个用例的 __GHUnit App_

在 App 中可以通过点击右上角的 Run 按钮运行全部用例,框架会查找所有以 testXXX 命名的无返回值方法,并执行。或点击 TableView 中的某个 Cell 运行单独的测试方法。

• 测试反馈

断言失败测试未通过的方法在 App 中会标记为红色,并给出每一个方法的运行时间。在 Console 中会打印出详细的出错信息,包括:异常类型,出错文件,位置,以及断言方法中指定的出错原因。更重要的是,出错时的程序堆栈内容(如图 12)。

图 __12_,未通过测试的方法,_Console__ 中的内容

GHUnit 通过 Console 中的内容给开发者提供帮助,可以快速定位程序出错的位置,这一点比 OCUnit 做的要好。

总结

GHUnit 在安装上确实显得有些麻烦,无法跟集成在 XCode 里的 OCUnit 相比。 但从开发者的角度讲,我更喜欢 GHUnit 带来的体验,GUI 的操作界面可以脱离 IDE 单独运行,支持运行单一测试方法和运行全部用例的,打印出错堆栈可以更快定位到问题所在。

本文简单介绍了两款框架的安装与入门,可以初步了解其各自特点,在接下来的文章中将会更加详细的介绍如何使用框架进行单元测试,以及框架中的一些高级功能。此外,后续还将向大家介绍另外的与这两款框架区别更加明显的单元测试框架。

作者简介:

高嘉峻(微博: @gaosboy ),SegmentFault.com 联合创始人,杭州 iOS 开发者沙龙发起人,资深 iOS 开发者。


感谢李永伦对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ )或者腾讯微博( @InfoQ )关注我们,并与我们的编辑和其他读者朋友交流。

2013-06-05 08:4732461

评论

发布
暂无评论
发现更多内容

沉寂唯品会3年,毅然辞职冲刺阿里,我是怎么备战金三银四的

Java 程序员 后端

泪目!跳槽太不容易,蚂蚁金服三轮面试,linux高可用集群架构毕业设计

Java 程序员 后端

涨姿势,Java中New一个对象是个怎么样的过程?,linux操作系统实用教程教师用书

Java 程序员 后端

淘系,60W年薪大牛!新肝出一份,细说JVM内存模型

Java 程序员 后端

毕业三年,从小公司到大厂,先后四面阿里,沈剑架构师训练营

Java 程序员 后端

极速体验docker容器健康,高并发分布式系统架构

Java 程序员 后端

架构师成长之路-docker 搭建es集群,rocketmq教程教程

Java 程序员 后端

栈实现队列(队列实现栈),java编程规范pdf百度云

Java 程序员 后端

每天一遍、阿里相见,Github爆火的spring高级源码解析免费下载

Java 程序员 后端

浅析Mysql索引数据结构演变,让你一看就懂,java业务场景面试题

Java 程序员 后端

来自北京大学NOIP金牌选手yxc的常用代码模板4——数学知识

Java 程序员 后端

查询太慢?看看ES是如何把索引的性能压榨到极致的!,java基础程序设计

Java 程序员 后端

毕业就想进大厂?啃完这篇阿里大神整理的Kafka神仙文档,从此面试底气十足

Java 程序员 后端

教学改革拆除“骨鲠”,产教融合的“一鱼多吃”创新之路

脑极体

元宇宙到底是个啥?

石云升

元宇宙 10月月更

Go channel,面试官会这样问

Rayjun

channel Go 语言

毕业两年面试阿里成功,月薪36k,分享一下我的经历,锁机制(本地锁+分布式锁)超全分析

Java 程序员 后端

深入浅出!全面剖析Java反射-Reflection,java项目开发实战入门电子书百度云

Java 程序员 后端

来自北京大学NOIP金牌选手yxc的常用代码模板3——搜索与图论

Java 程序员 后端

消息疯狂堆积!RocketMQ出Bug了?,rabbitmq分布式事务原理

Java 程序员 后端

消息队列面试题及答案,大V推荐

Java 程序员 后端

来自阿里大牛20000字总结-+-40张图文详解,我就不信你还参透不了并发编程

Java 程序员 后端

毕业两年,工作三年Java程序员的一点点体会与建议,java面试问项目流程

Java 程序员 后端

浅析Mysql索引数据结构演变,让你一看就懂(1),意外收获字节跳动内部资料

Java 程序员 后端

深入P8级别JAVA底层知识:你知道阿里P8需要掌握哪些技术吗?

Java 程序员 后端

深入浅出!带你重学Java—ArrayList,mongodb的存储原理

Java 程序员 后端

欲速则不达,欲达则欲速!,mysql主从复制原理两主一从ID

Java 程序员 后端

死磕Spring之AOP篇 - Spring AOP常见面试题,java高级开发工程师简历

Java 程序员 后端

每日一学:这个-RabbitMQ-必会-Routing路由模式,你学会了吗

Java 程序员 后端

测试用例的设计方法及案例,java技术框架

Java 程序员 后端

架构设计基础知识整理,java常用设计模式面试

Java 程序员 后端

iOS开发中的单元测试(一)_Android/iOS_高嘉峻_InfoQ精选文章