使用Xcode自带的单元测试
今年苹果推出的iOS8和Swift的新功能让人兴奋。同时,苹果对于Xcode的测试工具的改进却也会影响深远。现在我们来看下XCTest,Xcode内置的测试框架。以及,Xcode6新增的XCTestExpectation和性能测试。
现在Xcode项目已经支持out-of-the-box的测试。比如,创建一个新的iOS应用项目后,项目会自动配置两个顶层的group:一个是“应用名称”的group,一个是“项目名称Test”group。对应于这两个顶层的group的是两个target。一个运行的target,一个测试的target。项目自动生成的scheme也允许用户用Command+R运行应用,Command+U编译运行测试的target。
在测试的target中,默认的情况下只有一个叫做“应用名称Test”的文件。这个文件里面会包含一个XCTextCase类,里面有对setUp方法和tearDown方法的调用,还有一个示例的测试方法和性能测试的test case。
XCTestCase
Xcode的单元测试包含在XCTestCase子类中。组织测试的时候需要尽量考虑实际的应用操作流程。
setUp & tearDown
setUp方法在XCTestCase的测试方法调用之前调用。当测试全部结束之后调用tearDown方法。
class MVVMTests: XCTestCase {
override func setUp() {
super.setUp()
} override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
}
setUp方法可以在测试之前创建在test case方法中需要用到的一些对象等。tearDown方法则在全部的test case执行结束之后清理测试现场,释放资源删除不用的对象等。所以,setUp方法一般都是这么用的:
var calendar: NSCalendar?
var locale: NSLocale? override func setUp() {
super.setUp() self.calendar = NSCalendar(identifier: NSGregorianCalendar)
self.locale = NSLocale(localeIdentifier: "en_US")
}
XCTestCase的初始化不是用户控制的,所以属性在setUp方法中初始化的属性只能被定义为optonal的。不定义成optional的话,就只能在定义属性的时候直接给出初始化。如:
var calendar: NSCalendar = NSCalendar(identifier: NSGregorianCalendar)
var locale: NSLocale = NSLocale(localeIdentifier: "en_US")
功能测试
test case中的每一个方法都是test开头,这样容易辨识。方法中会执行断言(assertion),来判断这个测试是否通过。
func testExample() {
XCTAssertEqual( + , , "one plus one equals two")
}
常用的XCTest断言
XCTest会用到很多的断言,很多,但是只有一部分是常用到的。这里一一列出:
基本测试
所有的断言都是从最基本的这个断言演化出来的:
XCTAssert(expression, format...)
如果expression(表达式)执行的结果为true的话,这个测试通过。否则,测试失败,并在console中输出后面的format字符串。
后面基于XCTAssert演化出来的断言,不仅可以满足测试的需求而且可以更好更明确的表达出你要测试的是什么。最好是使用这些演化出来的断言,XCTestAssert不是必须最好不要用。
Bool测试
对于bool型的数据,或者只是简单的bool型的表达式,使用XCTestAssertTrue或者XCTestAssertFalse:
XCTAssertTrue(expression, format...)
XCTAssertFalse(expression, format...)
相等测试
测试两个值是否相等使用XCTAssert[Not]Equal:
XCTAssertEqual(expression1, expression2, format...)
XCTAssertNotEqual(expression1, expression2, format...)
XCTAssertGreaterThan[OrEqual] & XCTAssertLessThan[OrEqual], 和下面的条件操作符比较的是一个意思 == with >, >=, <, 以及 <=
在Double、Float型数据的对比中使用XCTAssert[Not]EqualWithAccuracy来处理浮点精度的问题:
XCTAssertEqualWithAccuracy(expression1, expression2, accuracy, format...)
XCTAssertNotEqualWithAccuracy(expression1, expression2, accuracy, format...)
Nil测试
使用XCTAssert[Not]Nil断言判断给定的表达式值是否为nil:
XCTAssertNil(expression, format...)
XCTAssertNotNil(expression, format...)
无条件失败断言
最后,XCTFail提供的是无条件断言:
XCTFail(format...)
XCTFail,无条件的都是测试失败。这个东东有什么用处呢。在测试驱动里有这么个情况,你定义了测试方法,但是没有给出具体的实现。那么你不会希望这个测试能通过的。是的,XCTFail就是这么个用途。一般被用作一个占位断言。等你的测试方法完善好了之后再换成最贴近你的测试的断言。有或者,在某些情况下else了之后就是不应该出现的情况。那么这个时候可以把XCTFail放在这个else里面。
性能测试
在Xcode6中新增的测试代码性能的功能:
func testPerformanceExample() {
let dateFormatter = NSDateFormatter()
dateFormatter.dateStyle = .LongStyle
dateFormatter.timeStyle = .ShortStyle let date = NSDate() self.measureBlock() {
let string = dateFormatter.stringFromDate(date)
}
}
测试结果:
Test Case '-[MVVMTests.MVVMTests testPerformanceExample]' started.
<unknown>::
Test Case '-[MVVMTests.MVVMTests testPerformanceExample]' measured [Time, seconds] average: 0.000, relative standard deviation: 257.209%, values: [0.000390, 0.000010, 0.000007, 0.000006, 0.000006, 0.000006, 0.000006, 0.000006, 0.000006, 0.000006], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100
Test Case '-[MVVMTests.MVVMTests testPerformanceExample]' passed (0.278 seconds).
性能测试可以帮助开发者建立一个主要功能的基本性能基线。确保这些主要的功能代码和算法能在这个性能基线内完成。
XCTestExpectation
Xcode单元测试中加入的最令人兴奋的功能也许就是类XCTestExpression类带入的异步测试了。现在测试可以等待指定长度的时间,一直到某些条件符合的时候在开始测试。而不用再写很多的GCD代码控制。
要使用异步测试,首先用方法expectationWithDescription创建一个expection。
let expectation = expectationWithDescription("...")
之后,在方法的最后添加方法waitForExpectationsWithTimeout,指定等待超时的时间和指定时间内条件无法满足时执行的closure。
waitForExpectationsWithTimeout() { (error) in
// ...
}
剩下的就是在异步测试剩下的回调函数中告诉expectation条件已经满足。
expectation.fulfill()
如果在测试中有多个expectation,则每个expectation都必须fulfill,否则测试不通过。
这里是一个测试异步网络访问的示例:
func testAsynchronousURLConnection(){
let URL = NSURL(string: "http://www.baidu.com")!
let expectation = expectationWithDescription("GET \(URL)") let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(URL, completionHandler: {(data, response, error) in
expectation.fulfill() // 告诉expectation满足测试了 XCTAssertNotNil(data, "返回数据不应该为空")
XCTAssertNil(error, "error应该为nil") if response != nil {
var httpResponse: NSHTTPURLResponse = response as NSHTTPURLResponse
XCTAssertEqual(httpResponse.URL!.absoluteString!, URL, "HTTPResponse的URL应该和请求URL一致")
XCTAssertEqual(httpResponse.statusCode, , "HTTPResponse状态码应该是200")
XCTAssertEqual(httpResponse.MIMEType as String, "text/html", "HTTPResponse内容应该是text/html")
}
else{
XCTFail("返回内容不是NSHTTPURLResponse类型")
}
}) task.resume() waitForExpectationsWithTimeout(task.originalRequest.timeoutInterval, handler: {error in
task.cancel()
})
}
使用Mock
有了异步测试的支持,Xcode快要把满足测试驱动开发的龙珠已经集齐了。但是,还差一个Mock!
Mock是一个很有用的东西。使用这个技术可以有效的分离那些不利于测试的因素,比如:过得复杂、不确定、性能约束等。比如遇到交互的网络交互、高强度的数据库查询或者某些存在资源竞争的状态等。
有很多的开源库支持Mock和Stub。但是这些库都严重的依赖于Objective-C运行时,所以在Swift下某些功能无法使用。在Swift下,类可以定义在一个类的方法中。这一特点允许mock自包含(self-contain)的对象。只要定义一个mock类,然后override必要的方法:
func testFetchRequestWithMockedManagedObjectContext() {
class MockNSManagedObjectContext: NSManagedObjectContext {
private override func executeFetchRequest(request: NSFetchRequest, error: NSErrorPointer) -> [AnyObject]? {
return [["name": "张三", "email": "zhangsan@apple.com"]]
}
} let mockContext = MockNSManagedObjectContext()
let fetchRequest = NSFetchRequest(entityName: "User")
fetchRequest.predicate = NSPredicate(format: "email ENDSWITH[cd] %@", "apple.com")
fetchRequest.resultType = NSFetchRequestResultType.DictionaryResultType var error: NSError?
let results = mockContext.executeFetchRequest(fetchRequest, error: &error) XCTAssertNil(error, "error应该为nil")
XCTAssertEqual(results!.count, , "fetch request应该只返回一个结构") let result = results![] as [String: String]
XCTAssertEqual(result["name"]! as String, "张三", "name应该是张三")
XCTAssertEqual(result["email"]! as String, "zhangsan@apple.com", "email应该是zhangsan@apple.com")
}
结论
Xcode6的内置工具终于足够的好了。也就是说即使是很大的APP也没有必要为了单元测试的代码覆盖率而排斥Xcode内置的测试工具。无论什么样的测试,XCTest的各种断言、expectation和性能测试都足够应对。但是无论多好的工具,都需要用好才行。
如果你在测试iOS或者OS X的APP,开始为自动添加的测试类添加一些断言并按下Command+U。你一定会发现感觉这些工具让你的测试方便不少!
示例代码在这里。
参考文章:http://nshipster.com/xctestcase/
使用Xcode自带的单元测试的更多相关文章
- 关于使用Xcode自带的单元测试UnitTest的介绍
什么是单元测试? 单元测试就是为你的方法专门多写一个测试函数.以保证你的方法在不停的修改开发中.保持正确.如果出错,第一时间让你知道,这样从最小单位开始监控来保证软件的质量. 什么时候用到单元测试: ...
- [Xcode 自带svn的使用]
xcode自带svn的使用 1.代码中 某文件后面有 “M” 标记,表示该文件已被修改,需要commit. (右键该文件 -> source control -> c ...
- Xcode自带iOS测试方法
在说Xcode自带测试方法前先讲下程序在内存中的空间划分, 一般可分为5个部分: #1. BSS段, 存放未初始化的全局变量. BSS是英文Block Started by Symbol的简称.BSS ...
- iOS-使用Xcode自带单元测试UnitTest
![Uploading QQ20160129-3_262826.png . . .]####什么是单元测试?一听到单元测试这个词感觉很高端,其实单元测试就是为你的方法多专门写一个测试函数.以保证你的方 ...
- Xcode 5 下的单元测试
新版Xcode 5和Server发布以后,apple对单元测试的支持是越来越好了.从这一点看出apple对单元测试的也是越来越重视了. 这篇Blog就简单的介绍这集成化测试功能. Server更新后是 ...
- [iOS翻译]《iOS7 by Tutorials》系列:在Xcode 5里使用单元测试(下)
4.测试失败的调试 是时候追踪之前测试失败的问题了.打开GameBoard.m,找到cellStateAtColumn:andRow: 和 setCellState:forColumn:andRow: ...
- [iOS翻译]《iOS7 by Tutorials》系列:在Xcode 5里使用单元测试(上)
简介: 单元测试是软件开发的一个重要方面.毕竟,单元测试可以帮你找到bug和崩溃原因,而程序崩溃是Apple在审查时拒绝app上架的首要原因. 单元测试不是万能的,但Apple把它作为开发工具包的一部 ...
- XCode下的iOS单元测试
XCode 内置了 OCUnit 单元测试框架,但目前最好用的测试框架应该是 GHUnit.通过 GHUnit + OCMock 组合,我们可以在 iOS 下进行较强大的单元测试功能.本文将演示如何在 ...
- 手机抓包xcode自带命令行工具配合wireshark实现
三.最佳方式:rvictl命令 优点:简单,而且可以抓所有网络接口的数据: 缺点:似乎没有,要求手机iOS5以上不算要求吧?如果说缺点,就是这个命令是Xcode的Command Line Tools ...
随机推荐
- js类型判断的方法
var arr=[]; alert(Object.prototype.toString.call(arr)=='[object Array]');
- 解决同时共用MOB公司的shareSDK和SMSSDK的冲突问题
现在mob的SDK版本升级到3.0,跟之前的版本不兼容,尤其是在 同时使用shareSDK和SMSSDK的时候会发生冲突,报NoSuchMethodException的错误. 只需要将所有的jar包和 ...
- 磁盘映射: between 宿主机 and 客户机
一.虚拟机映射到宿主机 在虚拟机关机的状态下,双击右侧设备栏里硬盘,在弹出的窗口中单击“实用程序“,选择“映射”.打开映射虚拟磁盘的窗口,其中的“卷”就是你希望映射虚拟机中的哪个分区到主机,如 ...
- 转:ios Sqlite数据库增删改查基本操作
研究了几天的数据库,终于把它给搞出来了.Sqlite是ios上最常用的数据库之一,大家还是有必要了解一下的.这是仿照网上的一个例子做的,有些部分写的不好,我稍作了修改,以讲解为主,主要让大家能够明白如 ...
- JAXB:Java对象序和XML互相转化的利器
JAXB(Java Architecture for XML Binding简称JAXB)允许Java开发人员将Java类映射为XML表示方式.JAXB提供两种主要特性:将一个Java对象序列化为XM ...
- WinForm控件学习笔记【第一天】——Control类
感悟:明天就又是学校双选会的日子了.两年我都参与了学校的双选会的服务工作,现在该是双选会服务的我时候了.怎么样找到一份好的工作,或者说怎么样学习才能符合企业对人才的要求,我现在也是很迷茫.平时都是在看 ...
- wapp HTTP Error 404. The requested resource is not found.
原因: 本地80端口被占用,需要修改WAMP的默认端口 修改设置: 找到 bin/apache/apache***/conf/httpd.conf文件 将文件中的80修改为8088 修改这两个地方端口 ...
- JavaScript对数组的处理(一)
数组创建 JavaScript中创建数组有两种方式,第一种是使用 Array 构造函数: var arr1 = new Array(); //创建一个空数组 var arr2 = new Array( ...
- springboot-mybatis-plus基本项目框架
此仅仅为web最基本框架, 统一异常管理.接口统一日志管理. 项目结构: 注: 修改为如下图,作用是sql打印输出. 源码下载:https://files.cnblogs.com/files/007s ...
- Objective-C语法之NSPredicate的使用
正则表达式判断手机号码和电话号码的方法: #import <Foundation/Foundation.h> /** 正则判断手机号码地址格式 */ BOOL isMobileNumber ...