1.前言
1.1测试的目的
- 在开发前期检验了代码逻辑的正确性,开发后期,无论是修改代码内部抑或重构,测试的结果为这一切提供了可量化的保障。
- 可以便捷的对某个具体方法进行功能测试。
- 对方法性能测试,如耗时测试
1.2适合测试场景
- 模块比较稳定的回归测试
- 极限操作的测试
- 长时间监控类测试
- 性能测试
2.方案选择
2.1 单元测试框架
常见的单元测试框架有XCTest 、GHUnit 、Kiwi
XCTest:与Xcode深度集成。而且可以享受Apple后续对XCTest升级的福利,配合OCMock使用是知名第三库常使用的方案
GHUnit:GitHub 上著名的开源测试框架,可视化、开源、扩展等功能,让其相比 XCTest 更加强大(现在的 XCTest 也很完善了, GHUnit 比较老,现在已经停止维护,不建议使用)
Kiwi:BDD框架,行为驱动框架,可读性较高,但重量级的,集 OCMock、Specta、Expecta 所拥有的功能与一身,最小测试单位为一个测试用例类,灵活性较低。
知名开源库的单元测试框架:
AFNetworking:XCTest
AsyncDisplayKit:XCTest+OCMock
FMDB:XCTest
FBKVOController:XCTest + OCMockito + OCHamcrest
个人推荐使用Xcode自带的XCTest+OCMock/OCMockito
2.2 UI测试框架
Appium:通过驱动UI Automation控制手机UI操作。但是,由于Xcode 8
以后UI Automation没有了,因此导致无法直接使用Appium。
KIF:KIF一个建立在XCTest的UI测试框架,能和应用的代码工程完美结合。通过accessibility来定位具体的控件,再利用私有的API来操作UI。很多大公司如Google都在使用这个测试框架。
个人推荐UI测试框架使用KIF
3.现有工程添加单元测试
3.1 创建单元测试Target
File->New->Target,然后选中“iOS”在搜索框里输入 test ,就能看到 “iOS UITesting Bundle”和 “iOS Unit Testing Bundle”两个图标了
选中后点击“Next”按钮后,分别创建如下两个taget:
KMeetingUnitTestDemoTests(单元测试,测试每一个类能够正常工作)和KMeetingUnitTestDemoUITests(UI测试,也叫做集成测试,从业务层的角度保证各个业务可以正常工作)
3.2 单元测试类介绍
- setUp 用于做一些初始化代码
- tearDown用于对象的销毁
- 所有测试类都必须继承XCTestCase
- 测试方法必须以testXXX开头,Xcode会自动识别出所有的测试方法
- 在一个类中测试方法的调用顺序是按照方法的顺序来调用的
调用
- 执行所有测试方法:command + u
- 只执行某个测试方法:点击方法前的菱形(目前是对号/错号)
- 执行某个类的所有测试方法:点击类前的菱形(目前是对号/错号)
3.3 简单使用
对ViewController添加如下方法,然后Common+U进行测试:
- (NSInteger)addA:(NSInteger)a andB:(NSInteger)b;
注:XCTAssertTrue(expression, format...)当expression求值为TRUE时通过,如果不通过则显示format。
3.4 方法性能测试
对比for in 与 enumerate遍历耗时,初始化数组
设置baseline0.03s
运行结果
3.5 OCMock的使用
当我们写单元测试的时候,不可避免的要去尽可能少的实例化一些具体的组件来保持测试既短又快。测试的方法引用很多外部依赖的对象时,用mock来替代实例化具体的依赖class可以保持单元的隔离。
参考:https://github.com/erikdoe/ocmock
PS:常用断言语句用法如下:
- 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时通过;
- XCTAssertEqualObjects(a1, a2, format...)判断相等,[a1 isEqual:a2]值为TRUE时通过,其中一个不为空时,不通过;
- XCTAssertNotEqualObjects(a1, a2, format...)判断不等,[a1 isEqual:a2]值为False时通过;
- XCTAssertEqual(a1, a2, format...)判断相等(当a1和a2是 C语言标量、结构体或联合体时使用, 判断的是变量的地址,如果地址相同则返回TRUE,否则返回NO);
- XCTAssertNotEqual(a1, a2, format...)判断不等(当a1和a2是 C语言标量、结构体或联合体时使用);
- XCTAssertThrows(expression, format...)异常测试,当expression发生异常时通过;反之不通过;
- XCTAssertThrowsSpecific(expression, specificException, format...) 异常测试,当expression发生specificException异常时通过;反之发生其他异常或不发生异常均不通过;
- XCTAssertThrowsSpecificNamed(expression, specificException, exception_name, format...)异常测试,当expression发生具体异常、具体异常名称的异常时通过测试,反之不通过;
- XCTAssertNoThrow(expression, format…)异常测试,当expression没有发生异常时通过测试;
- XCTAssertNoThrowSpecific(expression, specificException, format...)异常测试,当expression没有发生具体异常、具体异常名称的异常时通过测试,反之不通过;
- XCTAssertNoThrowSpecificNamed(expression, specificException, exception_name, format...)异常测试,当expression没有发生具体异常、具体异常名称的异常时通过测试,反之不通过
3.6 查看代码覆盖率
打开Edit Scheme,勾选Gather coverage for
运行用例,查看结果
点击展开相应的类,可查阅该类未测试到的方法。
4.UI集成测试
4.1 KIF框架介绍
- KIF的本质还是XCTest,它继承了XCTest在其基础上增加了UI测试的能力,提供了非常丰富的UI测试的接口。
4.2 KIF的使用
- 首先,基于KIFTestCase新建测试类,对一个简单的登录界面测试。虽然它生成的内容与XCTest相似,但事实上,由于KIF做了封装,原来用来做初始化的setUp()及tearDown()都不再需要了,所以KIF封装了几个更好用的接口,下面将进行介绍。
1.-(void)beforeAll。在本类中第一个test case执行前执行一次,用于执行当前类中各个测试函数的公共操作。(因为不能保证这个方法与test case是同一个类实例,所以不能用来设置实例变量的值,但是可以设置静态变量)
2.-(void)beforeEach。在每个test case执行前执行一次,用于执行各个函数需要的测试环境。因为这个方法与test case是同一个类实例,所以可以用来设置实例变量。
3.-(void)afterEach。在每个test case执行后执行一次,用来将App恢复至test case之前的状态,可以包含一些条件判断逻辑,从失败的test case中恢复,以确保不影响之后的测试。
4.-(void)afterAll。执行完测试类的最后一个test case后执行一次,一般用于将App恢复至测试的初始状态。
- 获取对应控件,并做一些UI操作
1. 根据Accessibility获取输入框控件
//根据Accessibility获取控件
UITextField *nameTF = (UITextField*)[tester waitForViewWithAccessibilityLabel:@"nameTF"];
UITextField *pwdTF = (UITextField*)[tester waitForViewWithAccessibilityLabel:@"pwdTF"];
注:需要在原始的类中设置Accessibility,如 [self.nameTF setAccessibilityLabel:@"nameTF"];
2.向控件输入内容
//输入内容
[tester clearTextFromAndThenEnterText:@"ABC" intoViewWithAccessibilityLabel:@"nameTF"];
[tester clearTextFromAndThenEnterText:@"123456" intoViewWithAccessibilityLabel:@"pwdTF"]
3.点击按钮
//点击控件
[tester tapViewWithAccessibilityLabel:@"loginBtn"];
4.等待特定控件出现
//10s后未出现预设控件,测试失败
[tester waitForViewWithAccessibilityLabel:@"loginBtn"];
更多UI操作,参考https://github.com/kif-framework/KIF
5.其他辅助框架
Expecta Or OCHamcrest
Expecta和OCHamcrest这两个都是断言的扩展框架,Expecta最近很少更新,相对老旧,OCHamcrest持续更新,扩展性更好,推荐使用OCHamcrest。
OHHTTPStubs
在需要伪造网络请求返回的数据,模拟网络请求时的慢网环境情况使用