单元测试:是对最小单元(方法、函数)的验证性测试。一般来说,单元测试不需要依赖外部环境、网络环境、数据库环境。
UI 测试:结合页面元素和交互流程,对应用业务进行测试。
快速(Fast):测试应该能快速运行。
独立(Independent):测试应该相互独立,不能有依赖。
可重复(Repeatable):测试应当可在任何环境重复通过。包括无网络的环境。
自足验证(Self-Validating):测试应该有布尔值输出。
及时(Timely):测试应及时编写。单元测试应该恰好在使其通过的代码之前编写。如果业务代码更改,测试代码也应该考虑是否随其变动
模块 | 备注 |
---|---|
算法相关 | 优先最高,保证算法正确性,避免后期改动造成错误 |
对外提供的 Router | 保证外部调整和唤起正常 |
业务逻辑 | 自行创建测试数据和条件参数测试 |
网络数据处理 | 通过 Mock 处理测试 |
ViewController | 通过 UI 测试 |
ViewModel/xxxUtil/xxxManager | |
全局方法、类方法 | |
本地json/配置文件 | app启动或者依赖资源,保证其可访问和完整性 |
以下部分不建议添加单元测试
Model 层、具体网络请求
UI测试,更注重保证回归线和业务正确性。
单元测试开发主要是基于以下三个要素:
**Given(条件):**配置测试的初始状态,比如造一些数据、mock一些对象等,这里我们可能需要OCMock这个库的辅助。
**When(测试条件):**对要测试的目标执行代码,调用你要测试的方法
**Then(结果检查):**对测试结果进行断言(成功 or 失败),对于结果做一个预判,并且通过编译来判断你的预判是否正确
- (void)testGetNameSpaceFromUUID{
NSDictionary *data = 0(0"aaa":@fo"type":@(@"namespace":@"bbb"})); // Given
NSString *nameSpace = [SCSCartInfoParser getNameSpaceFromUUID:@"aaa" withData:data]; // When
XCTAssert ([nameSpace isEqualToString:@"bbb"]); // Then
}
不要对私有方法进行测试(如果需要,请尝试拆离到单独的类)
UI测试更加关注交互和业务,用代码去模拟用户的行为。
比如通过按钮的sendActionsForControlEvents:
方法模拟点击,通过代理方法实现查看点击 cell 时间是否符合预期等。
由于 XCTest 适合简单情况,存在阅读性差的问题,所以同时引入 Kiwi 框架,在复杂类和测试中可使用 Kiwi 来编写单元测试。
XCTest
是自带的简单单元测试技术。
示例代码:
- (void)testRemoveTailOfZero {
NSString *givenStr1 = @"0.0023002300";
NSString *givenStr2 = @"000043003400";
NSString *givenStr3 = @"9.00";
NSString *givenStr4 = @"100.0";
XCTAssertTrue([[givenStr1 removeTailOfZero] isEqualToString:@"0.00230023"], @"移除末尾为0和.错误");
XCTAssertTrue([[givenStr2 removeTailOfZero] isEqualToString:@"000043003400"], @"移除末尾为0和.错误");
XCTAssertTrue([[givenStr3 removeTailOfZero] isEqualToString:@"9"], @"移除末尾为0和.错误");
XCTAssertTrue([[givenStr4 removeTailOfZero] isEqualToString:@"100"], @"移除末尾为0和.错误");
}
Kiwi是一个iOS平台十分好用的行为驱动开发(Behavior Driven Development,以下简称BDD)的测试框架,有着非常漂亮的语法,可以写出结构性强,非常容易读懂的测试。
优点
示例代码:
describe(@"NSString+Category", ^{
context(@"去除小数点末尾0", ^{
it(@"当0.0023002300时,应该去除后面两个0,即:0.00230023", ^{
[[@"0.0023002300".removeTailOfZero should] equal:@"0.00230023"];
});
it(@"当000043003400时,整数保持不便", ^{
[[@"000043003400".removeTailOfZero should] equal:@"000043003400"];
});
it(@"当9.00时,应该去除小数点后面两个0,即:9", ^{
[[@"9.00".removeTailOfZero should] equal:@"9"];
});
it(@"当100.0时,应该去除小数点后面一个0,即:100", ^{
[[@"100.0".removeTailOfZero should] equal:@"100"];
});
});
}
如过你打算使用 Kiwi,强烈建议你阅读以下教程:
TDD 的 iOS 开发初步以及 Kiwi 使用入门 – OneV
Kiwi 使用进阶 Mock, Stub, 参数捕获和异步测试 – OneV
可以理解为测试桩,它能实现当特定的方法被调用时,返回一个指定的模拟值。如果你的测试用例需要一个伴生对象来提供一些数据,可以使用 stub 来取代数据源,在测试设置时可以指定返回每次一致的模拟数据。
可以理解为侦查,它负责汇报情况,持续追踪什么方法被调用了,以及调用过程中传递了哪些参数。你能用它来实现测试断言,比如一个特定的方法是否被调用或者是否使用正确的参数调用,方法返回值。当你需要测试两个对象间的某些协议或者关系时会非常有用。
与 spy 类似,但在使用上有些许不同。spy 追踪所有的方法调用,并在事后让你写断言,而 mock 通常需要你事先设定期望。你告诉它你期望发生什么,然后执行测试代码并验证最后的结果与事先定义的期望是否一致。
按照 TTD 开发流程,应该先写测试用例,再编写组合业务代码。但是也存在待测试先行,在编写待测试代码时,我们应该遵循以下原则:
传统 MVC 模式容易导致大量逻辑代码存在于 ViewContoller 中,大量私有方法导致无法进行单元测试。
对此,我们应该抽离中间件,用来处理数据和逻辑处理,通过中间件来进行单元测试,同时,使 ViewController 更加简洁和单一职责。
通过勾选 Xcode 的 CodeCoverage可以查看单元测试覆盖率和覆盖部分。
查看 Build Settings
如果需要配合自动化,可以借助 XcodeCoverage 库导出覆盖率查看
待完成
优点:可以多语言, 有图形化操作
缺点:配置复杂。
配置参考
XCTest – 官方文档
[S01E02]iOS 单元测试
iOS 单元测试和 UI测试教程–raywenderlich
英文教程,从理论到实践操作。
Github+Fastlane+Jenkins 的持续集成测试–raywenderlich
可测试性工程
WWDC17官方视频讲解,介绍我们应该够如果编写可测试的代码。
京东多端全流程交易解决方案阿波罗平台iOS单元测试实践
通过集成化工具 Bamboo 和 XcodeCoverage 查看测试覆盖率。如何写一个单元测试,OCMock 基本介绍和使用
TDD 的 iOS 开发初步以及 Kiwi 使用入门 – OneV
Kiwi 使用进阶 Mock, Stub, 参数捕获和异步测试 – OneV
王巍编写的对 Kiwi 框架的理解和使用,如何利用 Kiwi 中的 Mock和 stub。示例包含了对 Kiwi 和原始 XCTest 使用。
相关 Demo
Demo VVStack: 同时使用 Kiwi 和 XCTest 进行单元测试对比
PhotoData_Kiwi:将 objc.io 中测试相关的 Demo 使用 Kiwi 替换,详细描述对类初始化及各个公共方法的测试,以及使用 stub 和 mock 测试 Controller。
objc.io 中国 测试相关文章
行为驱动测试 – objc.io
对objc.io文章的翻译,其中的测试板块内容,包含基本测试理论,单元测试实际集成经验等内容,非常建议阅读。
iOS自动化测试 UITests
主要介绍对元素的操作。