写点什么

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:4733032

评论

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

京东丨阿里丨携程面试总结,已成功拿到京东offer

Java架构师迁哥

前端项目配置ts,axios,router,vuex

Vue js ts vuex VueRouter

谈谈测试环境管理与实践

大卡尔

测试环境 工程效能

吴凡 ベ莫离: 网友都说MyBatis多表查询太难了,小白:就这?我都学会了

牛哄哄的java大师

如何高效率的度过一天?

程序员海军

效率 方法论

模板格式不统一?百度AI产品经理为你讲解如何高效构建定制化OCR模型

百度大脑

百度 AI OCR

Map在Java 8中增加非常实用哪些函数接口?

xcbeyond

Java java8 5月日更 内容合集

要不要去创业?

石云升

创业 5月日更

硬核资源!清华博士的Spring Boot中AOP与SpEL笔记,码农:膜拜

牛哄哄的java大师

Java

Vue 组件通信的 8 种方式

程序员海军

Vue 大前端 组件通信 引航计划

深入了解 JavaScript 对象

程序员海军

JavaScript 大前端 对象

2021金三银四(拿下5个offer)面试经历,附阿里4面+京东4面【面经分享】

Java 编程 程序员 面试 计算机

北大学霸!手抄万字Java数组笔记,2小时吃透,你确定不拿走?

牛哄哄的java大师

Java 后端

架构实战营详细架构设计文档模板

Geek_e0c25c

Golang中runtime包的基本使用方式

liuzhen007

Go 语言 5月日更

Nginx基础配置-基础模块配置

梁龙先森

nginx 大前端

一击必杀!内网渗透——对不出网目标的精准打击

Thrash

安全

编程规范的意义

顿晓

5月日更 编程规范

Dubbo 服务分组与多版本

青年IT男

从操作系统底层的IO原理入手讲解,同时提供高性能开发的实战案例!美团大佬最新总结的1053页Java高并发核心编程笔记!

Java架构之路

Java 程序员 架构 面试 编程语言

spring boot项目TPS压测性能优化

李日盛

Spring Boot 性能调优

涵盖了Java基础+JVM+多线程并发编程+spring全家桶+Linux+数据结构+数据库+nginx+分布式,这份Java技术成长笔记太强了

Java架构之路

Java 程序员 架构 面试 编程语言

2021,国产数据库人的最好时代

BinTools图尔兹

数据安全 数据库管理 国产数据库

谷歌大佬的LeetCode算法刷题笔记,详细讲解了刷 LeetCode 时常用的技巧。

Java架构之路

Java 程序员 架构 面试 编程语言

Golang 实现 RTP

天黑黑

音视频 rtp Go 语言

所谓软件测试工作能力强,其实就是这5点

程序员阿沐

软件测试 自动化测试 测试工程师 黑盒测试 白盒测试

如有神助!阿里P7大牛把Spring Boot讲解得如此透彻,送你上岸

飞飞JAva

区块链是什么意思?源中瑞开发BaaS平台促进企业数字转型升级

源中瑞-龙先生

企业数字化转型 #区块链# 源中瑞 Baas

第八大洲环游记(三):人间胜境新西兰,AI孤岛or方舟?

脑极体

网络攻防学习笔记 Day6

穿过生命散发芬芳

5月日更 网络攻防

Redis - 哈希表

旺仔大菜包

redis

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