单元测试,又称模块测试。是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作。面向过程编程,一个单元就是单个程序;面向对象编程,程序最小单位是方法。
XCTest是Xcode内置的测试框架。同时,Xcode6新增XCTestExpectation和性能测试。
xcode6创建一个新的工程后,项目会自动配置两个group:一个是“工程名称”group;另一个是“工程名称Test”group。它们分别对应两个target,一个是运行的target,另一个是测试的target。用户使用Command+R编译运行的target,使用Command+U编译测试的target。
在测试的group中,有一个工程名称test.m文件,这个文件里面包含一个XCTestCase类,里面有四个方法:setUp、tearDown、testExample、testPerformanceExample。
XCTestCase
xcode的单元测试包含在XCTestCase的子类中,组织测试的时候尽量需要考虑实际的应用操作流程。
setUp & tearDown
setUp方法在XCTestCase的测试方法调用之前调用。当全部测试结束之后调用tearDown方法。
- (void)setUp { [super setUp]; // Put setup code here. This method is called before the invocation of each test method in the class. } - (void)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执行结束后清理测试现场,释放资源、删除不用的对象等。
功能测试
test case中的每一个方法都是以test开头,这样容易辨别。方法中会执行断言Assert来判断这个测试是否通过。
- (void)testExample { // This is an example of a functional test case. XCTAssert(YES, @"Pass"); }
常用的XCTest断言
XCTest会用到很多断言,这里列举一部分。
基本测试
所有的断言都是从最基本的这个断言演化出来的:
XCTAssert(expression,format……)
如果expression(表达式)执行结果为true的话,这个测试通过;否则,测试失败,并在控制台输出后面的format字符串。
Bool测试
对于bool型的数据,或者只是简单的bool型表达式,使用XCTestAssertTrue或XCTestAssertFalse。
相等测试
测试两个值是否相等使用XCTAssert[NO]Equal
XCTAssertEqual(expression1,expression2,format……)
在处理double、float类型数据的对比时使用XCTAssert[Not]EqualWithAccuracy来处理浮点精度的问题。
nil测试
使用XCTAssert[Not]Nil来判断给定的表达式值是否为nil。
无条件失败断言
使用XCTFail测试无条件断言
XCTFail(format……)
XCTFail,无条件的都是测试失败。这个东东有什么用处呢?在测试里面有这么个情况,我们定义了测试方法,但是没有给出具体的实现,那么我们肯定不会希望这个测试能顺利通过。是的,XCTFail就是这个用途。一般被用作一个占位断言,等我们的测试方法完善好了之后在换成最贴近我们测试的断言。再或者,在某些情况下else了之后就是不应该出现的情况,那么这个时候可以将XCTFail放在这个else里面。
性能测试
性能测试可以帮助开发者建立一个主要功能的基本性能基线,确保这些主要功能代码和算法能在这个性能基线内完成。
XCTestExpectation
XCTestExpectation是xcode自带的异步测试类。即现在测试可以等待指定长度的时间,一直到某些条件符合的时候再开始测试,而不用再写很多的GCD代码控制。
要使用异步测试,首先用方法expectationWithDescription创建一个expection。
let expectation = expectationWithDescription("……")
之后在方法的最后添加方法waitForExpectationsWithTimeOut,指定等待超时的时间和指定时间内条件无法满足时执行的closure。
waitForExpectationsWithTimeOut(10) {(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, 200, "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() }) }
总结
xcode内置工具已经很好了,即使很大的app也没有必要为了单元测试的代码覆盖率而排斥xcode内置工具。即无论怎么样的测试,XCTest的各种断言、expectation和性能测试都足以应付。
如果在测试IOS或者OS X的APP,开始为自动添加的测试类添加一些断言并按下Command+U。我们就会发现xocde自带的工具让我们测试很方便。
断言测试
测试方法的要求:1>必须无返回值;2>以test开头
测试方法执行的顺序:以方法名中test后面的字符大小有关,例如:
-(void)test001XXX会优于-(void)test002XXX执行
运行单元测试快捷键:Command+U
下面一共有18个断言(SDK中也是18个)
XCTFail(format……)生成一个失败的测试
XCTAssertNil(a1,format……)判空,a1为空时通过,反之不通过。
XCTAssertNotNil(a1,format……)判不为空,a1不为空时通过,反之不通过
XCTAssert(expression,format……)当expression求值为true时,测试通过。
XCTAssertTrue(expression,format……)当expression求值为true时,测试通过
XCTAssertFalse(expression,format……)当expression求值为False时,测试通过
XCTAssertEqual(a1,a2,format……)判断相等,相等时测试通过
XCTAssertNotEqual(a1,a2,format……)判断不等,不等时测试通过
XCTAssertEqualObjescts(a1,a2,format……)判断相等,[a1 isEqual:a2]值为True时通过,其中一个为空时不通过。
XCTAssertNotEqualObjescts(a1,a2,format……)判断不等,[a1 isEqual:a2]值为False时通过
XCTAssertEqualWithAccuracy(a1,a2,accuracy,format……)判断相等,(double或float类型)提供一个误差范围,在误差范围(+/-accuracy)以内相等时测试通过。
XCTAssertNotEqualWithAccuracy(a1,a2,accuracy,format……)判断不等,(double或float类型)提供一个误差范围,在误差范围(+/-accuracy)以内不等时测试通过。
XCTAssertThrows(expression,format……)异常测试,当expression发生异常时通过,反之不通过
XCTAssertNotThrows(expression,format……)异常测试,当expression没有发生异常时通过,反之不通过
XCTAssertThrowsSpecific(expression, specificException, format...) 异常测试,当expression发生specificException异常时通过;反之发生其他异常或不发生异常均不通过
XCTAssertNoThrowSpecific(expression, specificException, format...)异常测试,当expression没有发生具体异常、具体异常名称的异常时通过测试,反之不通过
XCTAssertThrowsSpecificNamed(expression, specificException, exception_name, format...)异常测试,当expression发生具体异常、具体异常名称的异常时通过测试,反之不通过
XCTAssertNoThrowSpecificNamed(expression, specificException, exception_name, format...)异常测试,当expression没有发生具体异常、具体异常名称的异常时通过测试,反之不通过
举例子应用
//1.比较基本数据类型
XCTAssertEqual(1,2,@"a1 = a2 shoud be true");//测试没通过
XCTAssertEqual(1,1,@"a1 = a2 shoud be true");//测试通过
//2.如果a1,a2是指针,则当他们指向同一个对象时才会返回YES,如下:
NSArray *array1 = @[@1];
NSArray *array2 = @[@1];
NSArray *array3 = array1;
XCTAssertEqual(array1,array2,@"a1 and a2 shoulid point to the same object");//测试不通过
XCTAssertEqual(array1,array3,@"a1 and a2 shoulid point to the same object");//测试通过
//3.要注意使用NSString
NSString *str1 = @"1";
NSString *str2 = @"2";
NSString *str3 = str1;
XCTAssertEqual(str1,str2,@"a1 and a2 shoulid point to the same object");//测试通过
XCTAssertEqual(str1,str3,@"a1 and a2 shoulid point to the same object");//测试通过
尽管str1与str2指向不同的对象,但是二者的指针比较依然通过了测试。由于str1与str2指向同一个常量,常量在data段中地址是固定的,所以二者地址相同。