iOS中的单元测试(一)

转载自http://www.infoq.com/cn/articles/ios-unit-test-1

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

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

OCUnit

相关厂商内容

基于Docker的DevOps ——QCon上海2014主题演讲

豌豆荚架构师周爱民加入ArchSummit北京2014大会组委会

AWS技术演讲:AWS设计模式在Autodesk的实现

如何构建工程师文化团队

白皮书下载:发布管理的痛点与最佳实践

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单独运行,支持运行单一测试方法和运行全部用例的,打印出错堆栈可以更快定位到问题所在。

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

时间: 2024-10-11 04:50:57

iOS中的单元测试(一)的相关文章

iOS开发中的单元测试(三)——URLManager中的测试用例解析

本文转载至 http://www.cocoachina.com/cms/plus/view.php?aid=8088 此前,我们在<iOS开发中的单元测试(一)&(二)>中介绍了从使用者的角度对比当下比较流行的两款单元测试框架OCUnit和GHUnit,这篇文章中我们将介绍一款导航控件URLManager. URLManager是一个基于UINavigationController和UIViewController,以URL Scheme为设计基础的导航控件,目的是实现ViewCont

iOS中JSONModel的使用

iOS中JSONModel的使用 流弊的JSON数据模型框架 https://github.com/jsonmodel/jsonmodel 版本 1.3.0 如果你喜欢JSONModel,并且使用了它,请你: star一下 给我一些反馈. 多谢! JSONModel for iOS and OSX JSONModel 是一个能够快速巧妙的创建数据模型的库. 你可以在 iOS or OSX APP中使用它. JSONModel 自动检查JOSN模型和结构体, 彻底的减少你的代码量. 添加JSONM

软件测试、ios中的测试概念以及步骤

软件测试: 软件测试的目标是应该服务于软件项目的目标,可以通过建议反馈使用更加高效的方法和工具,提升软件开发效率以及软件开发质量.同时还可以通过过一些手段,更早.更快.更多地发现缺陷,从容降低了这些缺陷带来的风险. 软件测试是为了消除更加多的bug,虽然没有能够消除彻底. 但是软件的质量不是通过软件测试,而是软件开发工程师决定的. 软件测试的好处: 1.测试可以对软件预防,然后反应给软件开发工程师,所以及时快速的找到错误. 2.对软件缺陷的检测. 软件测试的类型: 单元测试(Unit Tests

iOS开发之单元测试

开始之前 本文侧重讲述如何在iOS程序的开发过程中使用单元测试.使用Xcode自带的OCUnit作为测试框架. 一.单元测试概述 单元测试作为敏捷开发实践的组成之一,其目的是提高软件开发的效率,维持代码的健康性.其目标是证明软件能够正常运行,而不是发现bug(发现bug这一目的与开发成本是正相关的,虽然发现bug是保证软件质量的一种手段,但是很显然这与降低软件开发成本这一目的背道而驰).它是对软件质量的一种保证,例如重构之后我们需要保证软件产品的正常运行. 很多人认为编写单元测试没有用是认为单元

iOS中几种数据持久化方案

概论 所谓的持久化,就是将数据保存到硬盘中,使得在应用程序或机器重启后可以继续访问之前保存的数据.在iOS开发中,有很多数据持久化的方案,接下来我将尝试着介绍一下5种方案: plist文件(属性列表) preference(偏好设置) NSKeyedArchiver(归档) SQLite 3 CoreData 沙盒 在介绍各种存储方法之前,有必要说明以下沙盒机制.iOS程序默认情况下只能访问程序自己的目录,这个目录被称为"沙盒". 1.结构 既然沙盒就是一个文件夹,那就看看里面有什么吧

iOS中UIWebView的使用详解

iOS中UIWebView的使用详解 一.初始化与三种加载方式 UIWebView继承与UIView,因此,其初始化方法和一般的view一样,通过alloc和init进行初始化,其加载数据的方式有三种: 第一种: - (void)loadRequest:(NSURLRequest *)request; 这是加载网页最常用的一种方式,通过一个网页URL来进行加载,这个URL可以是远程的也可以是本地的,例如我加载百度的主页:     UIWebView * view = [[UIWebView al

IOS中NSString的常见用法

iOS NSString的常用用法 //1.创建常量字符串. NSString *astring = @"This is a String!"; //2.创建空字符串,给予赋值. NSString *astring = [[NSString alloc] init]; astring = @"This is a String!"; //3.在以上方法中,提升速度:initWithString方法 NSString *astring = [[NSString allo

iOS中UITableViewCell的重用问题解决方案

UITableViewCell重用 为了能够保证tableViewCell能够高效的执行,Objective-c中引进了重用队列的机制,重影现象也是在重用队列时经常遇到的问题,那么如何解决这个问题呢?下面给出了几种解决办法. 第一种解决方法 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { NSArray *subViews = cell

iOS 中UIButton的 settitle 和 titlelabel的使用误区

UIButton中设置Titl方法包括以下几种: - (void)setTitle:(NSString *)title forState:(UIControlState)state; - (void)setAttributedTitle:(NSAttributedString *)title forState:(UIControlState)state @property(nonatomic,readonly,retain) NSString *currentTitle; @property(n