单元测试,从陌生到熟悉
- 什么是单元测试?
相信我们做开发这么久,对这个也比较陌生,听起来很高端的样子。其实单元测试就是为你的方法多专门写一个测试函数。以保证你的方法在不停的修改开发中。保持正确。如果出错,第一时间让你知道,这样从最小单位开始监控来保证软件的质量。
- 什么时候用到单元测试
1、写完代码以后:想要验证一下自己写的代码是否有问题。
2、写代码之前:就是写代码之前所有的功能分模块的设计好,测试通过了再写。
3、修复某个bug后:一般修复完某个bug,为了确保修复是成功的,会写测试。
- 废话少说了,立刻上实例! XCTest 的简单用法
开始
在xcode新建项目中,默认都会给你建一个单元测试的target,如果你的项目没有,可以在导航中选择 File->New->Target->选择ios-test->选择iOS Unit Testing Bundle 新建一个测试target。所有的测试类都必须继承 XCTestCase 类。
其中有几点需要大家注意:
- 该类中以test开头的方法且void返回类型的方法都会变成单元测试用例(我们编写自定义的测试方法也必须用 test 开头,系统才能识别)
- 单元测试类继承 XCTestCase,每个类都有很重要的三个方法
setUp
taerDown
measureBlock
//每次测试前调用,可以在测试之前创建和初始化在test case方法中需要用到的一些对象等
- (void)setUp ;
//每次测试结束时调用tearDown方法,释放掉我们创建的资源
- (void)tearDown ;
//性能测试方法,通过测试block中方法执行的时间,比对设定的标准值和偏差觉得是否可以通过测试
measureBlock
断言
大部分的测试方法使用断言决定的测试结果。所有断言都有一个类似的形式:比较,表达式真假,强行失败,设定固定阈值等。断言的形式有如下 api:
//通用断言
XCTAssert(expression, format...)
//常用断言:
XCTAssertTrue(expression, format...)
XCTAssertFalse(expression, format...)
XCTAssertEqual(expression1, expression2, format...)
XCTAssertNotEqual(expression1, expression2, format...)
XCTAssertEqualWithAccuracy(expression1, expression2, accuracy, format...)
XCTAssertNotEqualWithAccuracy(expression1, expression2, accuracy, format...)
XCTAssertNil(expression, format...)
XCTAssertNotNil(expression, format...)
XCTFail(format...) //直接Fail的断言
举个小栗子:
- (void)testAge
{
_p.age = -10;
XCTAssertTrue(_p.age > 0,"年龄不能是负数");
}
- (void)testWeight
{
_p.weight = 50.0;
[_p keepfit:10.0];
XCTAssertEqual(40, _p.weight,"成功减重10公斤");
}
一个测试用例比较规范的写法,1:定义变量和预期,2:执行方法得到实际值,3:断言
性能测试
性能测试主要使用 measureBlock
方法,用于测试某组方法的执行时间,可以通过设置baseline(基准)
和stddev(标准偏差)
来判断方法是否能通过性能测试:
- (void)testPerformanceExample {
// This is an example of a performance test case.
[self measureBlock:^{
//Put the code you want to measure the time of here.
//需要进行性能测试的代码和方法
}];
}
异步测试
测试异步方法时,因为结果并不是立刻获得,所以在异步方法测试有一些特殊的api 和技巧。
举个大栗子:
- (void)testAsynExample {
XCTestExpectation *exp = [self expectationWithDescription:@"这里是操作出错的原因描述。。。或者其它描述"];
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
[queue addOperationWithBlock:^{
//模拟这个异步操作需要2秒后才能获取结果,比如一个异步网络请求
sleep(2);
//模拟获取的异步操作后,获取结果,判断异步方法的结果是否正确
XCTAssertEqual(@"a", @"a");
//如果断言没问题,就调用fulfill宣布测试满足
[exp fulfill];
}];
//设置延迟多少秒后,如果没有满足测试条件就报错
[self waitForExpectationsWithTimeout:3 handler:^(NSError * _Nullable error) {
if (error) {
NSLog(@"Timeout Error: %@", error);
}
}];
}
这个测试肯定通过的,因为设置延迟为3秒,而异步操作2秒就给出了一个正确的结果,并宣布了条件满足[exp fulfill],但是当我们把延迟改成1秒,这个测试用例就不会成功,错误原因是 expectationWithDescription:@"这里操作出错的原因描述。。。
。
异步测试还可以使用expectationForPredicate
和expectationForNotification
这两个方法。
执行测试快捷键
- command + 5 切换到测试选项卡后出现很多小箭头,点击可以单独测试或整体测试
- command + U运行整个单元测试
注意点
- 使用 pod 的项目中,在 XC 测试框架中测试内容包含第三方包的时候,需要手动去设置
Header Search Paths
才能找到头文件,还需要设置test target
的PODS_ROOT
路径。 - Xcode7及以上版本使用真机跑测试的时候,必须配好证书,否则会报
exc_breakpoint
错误。
UI Test
UITest 是随着 Xcode7被推出来的,主要功能是页面的 UI 测试,相比以前使用 JavaScript编写 的 UIAutomation(已被弃用),UITest 可以用 OC 或 Swift 我们熟悉的语言编写 UI 测试代码。UITest 最大的亮点是支持屏幕录制--通过对 app 的操作,自动生成相应的测试脚本代码。
UI Test 的工作原理,对于一般的 UIKit 对象,Apple 提供 XCUIElement 对象作为映射。我们只能通过 Accessibility 的 identifier 等属性来获取 UI 信息。
-
UITest 核心的类有三个
XCUIApplication
,XCUIElement
,XCUIElementQuery
- XCUIApplication是Application的代理,用来启动或者是终止UI测试程序.启动参数设置,在获取程序中的UI元素。
- XCUIElement是XCTest.framework对应用中的所有UI控件的抽象,所有的UI控件都是此类型,滑动,点击,轻扫手势
- XCUIElementQuery是查询UI的类,通过类似key-value的机制得到XCUIElement的实例,使用Type(XCUIElementType枚举),Predicate,Identifier创建query,使用elementAtIndex:, elementMatchingPredicate,elementMatchingType: identifier:方法访问匹配到的UI元素,同样遵循了XCUIElementTypeQueryProvider协议.
UI Test 主要是解放我们的双手,不用每次测试一个 UI 业务逻辑,就反复启动 app 的去点击跳转等。
整个 UI Test 中最难的部分,当属于查找 UI 元素,其它操作看起来就是自然语言一般,找到哪个控件,执行什么交互动作。
通过上述,示例,我们可以开始愉快的为我们的程序编写单元测试和录制 UI 测试的代码了(虽然以前很不情愿写测试代码,但是哭着也要写完啊。)
参考: