一、简介
单元测试通常由软件开发人员自己编写,他们将确认具体功能是否按照设计要求正常工作。单元测试的目标是隔离代码的每个部分,并确保每个独立的部分都能正常工作。
例如,如果你有一个计算器应用程序,你可能会为加法、减法、乘法和除法等每个功能编写单元测试,以确保当给定特定输入时,这些功能能返回正确的结果。
这种测试方法可以帮助找出代码中的错误和问题,确保在整个软件系统中的各个单位都能正常工作,从而提高了代码质量和可维护性。
在 iOS 开发中 XCTest
是 Apple
提供的官方测试框架,用于进行单元测试、性能测试以及用户界面测试。
以下是关于 XCTest
的详细介绍:
- 主要组件:
XCTest
框架包括XCTests
(单元测试)、XCUItests
(用户界面测试)和XCPPerformanceTests
(性能测试)。- 单元测试(
XCTests
):这是最基础的测试类型,主要用于测试应用中的个别方法或计算逻辑是否按预期工作。- 用户界面测试(
XCUItests
):这种测试模拟用户与应用程序的交互操作,例如点击按钮、滑动屏幕等。该测试可确保当用户使用您的应用时,界面和交互功能能正常运行。- 性能测试(
XCPPerformanceTests
):这种测试帮助您量化代码的性能,并在代码更改后跟踪其变化。您可以为某些任务设置基准时间,然后在优化代码后比较新的执行时间。- 测试断言:
XCTest
提供了一套断言供你验证测试结果。这些断言包括XCTAssertTrue()
,XCTAssertFalse()
,XCTAssertEqual()
等。- 集成和运行:
XCTest
完全集成在Xcode
中,且易于使用。你可以直接从Xcode
的测试导航器运行测试,或者使用快捷键Cmd + U
运行所有测试。- 测试报告:
Xcode
会为执行的XCTest
总的来说,XCTest
二、如何项目中添加
在 Xcode
中添加一个新的 XCTest
1、创建项目是直接创建
创建项目成功过项目目录下即可看到对应的单元测试文件夹
带有 Tests 后缀的文件夹
2、已有的项目添加 XCTest
3、运行 - (void)testExample
方法一直报错 Test Failed
报错 The bundle “...Tests” couldn’t be loaded. Try reinstalling the bundle.
需要设置 team 和 项目的 team 一致来解决
4、'XCTest/XCTest.h' file not found
问题描述:fatal error: 'XCTest/XCTest.h' file not found
解决方法
在报错的
Target
中的Building settings
中FRAMEWORK_SEARCH_PATHS
添加$(PLATFORM_DIR)/Developer/Library/Frameworks
5、运行项目报错 Library not loaded: @rpath/XCTest.framework/XCTest
三、 方法简单概述:
setUp
方法是当前测试类的初始化方法,我们可以将一些资源准备工作在这个方法中完成;tearDown
方式在测试结束后会调用,用来进行资源的清理。测试函数都需要以text
开头,testExample
是默认生成的一个测试用例函数,可以编辑testXXX
方法,只要是test
开头的方法都可以进行检查。xCTest
框架提供了众多测试断言,可以用来测试,命中断言,则认为当前测试用例失败。testPerformanceExample
是性能测试的一个案例,其内的measureBlock
里的代码会被默认执行10次,最终输出每次执行的时间消耗报告。
1.测试函数的要求是:1.必须无返回值;2.实例方法要求:没有入参,没有返回值,以 test
开头。
2.测试函数执行的顺序:以函数名中 test
后面的字符大小有关,比如 -(void)test001XXX
会先于 -(void)test002XXX
执行;
3.运行单元测试的快捷键:CMD + U
;
4.下面一共18个断言(SDK中也是18个,其含义转自 ios UnitTest 学习笔记,真心佩服原文的博主):
`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语言标量、结构体或联合体时使用,实际测试发现 `NSString` 也可以);
`XCTAssertNotEqual(a1, a2, format...)` 判断不等(当a1和a2是 C语言标量、结构体或联合体时使用);
`XCTAssertEqualWithAccuracy(a1, a2, accuracy, format...)` 判断相等,(`double`或`float`类型)提供一个误差范围,当在误差范围(`+/-accuracy`)以内相等时通过测试;
`XCTAssertNotEqualWithAccuracy(a1, a2, accuracy, format...)` 判断不等,(`double`或`float`类型)提供一个误差范围,当在误差范围以内不等时通过测试;
`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` 没有发生具体异常、具体异常名称的异常时通过测试,反之不通过
特别注意下 XCTAssertEqualObjects
和 XCTAssertEqual
。
`XCTAssertEqualObjects(a1, a2, format...)` 的判断条件是 `[a1 isEqual:a2]` 是否返回一个 `YES`。
`XCTAssertEqual(a1, a2, format...)` 的判断条件是`a1 == a2`是否返回一个 `YES`。
对于后者,如果a1和a2都是基本数据类型变量,那么只有a1 == a2
才会返回YES
。例如下面代码中只有第二行可以通过测试:
// 1.比较基本数据类型变量
XCTAssertEqual(1, 2, @"a1 = a2 shoud be true"); // 无法通过测试
XCTAssertEqual(1, 1, @"a1 = a2 shoud be true"); // 通过测试
但是,如果a1和a2都是指针,那么只有a1和a2指向同一个对象才会返回YES
。例如下面的代码中:
// 3.比较NSArray对象
NSArray *array1 = @[@1];
NSArray *array2 = @[@1];
NSArray *array3 = array1;
XCTAssertEqual(array1, array2, @"a1 and a2 should point to the same object"); // 无法通过测试
XCTAssertEqual(array1, array3, @"a1 and a2 should point to the same object"); // 通过测试
array1和array2指向不同对象,无法通过测试。
这里比较奇怪的是,NSString
另当别论:
// 2.比较NSString对象
NSString *str1 = @"1";
NSString *str2 = @"1";
NSString *str3 = str1;
XCTAssertEqual(str1, str2, @"a1 and a2 should point to the same object"); // 通过测试
XCTAssertEqual(str1, str3, @"a1 and a2 should point to the same object"); // 通过测试
尽管str1和str2指向不同的对象,但是二者的指针比较却能通过测试。
由于str1和str2指向同一常量,常量在内存的 data
段中地址是固定的,所以二者地址相同。
四、异步函数的测试
前面我们演示的测试用例所执行的逻辑都是同步的,但在实际的项目中,异步的操作很多,
- (void)requestData:(void (^)(BOOL))complete {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if (complete) {
complete(YES);
}
});
}
测试用例如下:
- (void)testAsync {
XCTestExpectation *except = [self expectationWithDescription:@"异步请求测试用例"];
[self.viewModel requestData:^(BOOL success) {
XCTAssertTrue(success);
[except fulfill];
}];
[self waitForExpectationsWithTimeout:10 handler:^(NSError * _Nullable error) {
}];
}
XCTestExpectation
可以理解为一个期望对象,当使用此对象调用 fulfill
方法后,表示异步逻辑完成。
五、代码覆盖率
与单元测试相关的,还有一个重要的概念:代码覆盖率。代码覆盖率是指在整个测试执行过程中,覆盖到的功能函数与所有功能函数的比例。覆盖率越高说明测试涉及的功能越全。
测试完成后,可以直接在Xcode
中查看代码覆盖率,如下图所示:
单元测试保持较高的覆盖率是非常重要的,其从另一个方面也是测试质量的保障。【当然这里是一个示例,所以没有很高的覆盖率】
如果看不到代码覆盖率,点击edit scheme
,看对应的tagart-->test-->code Coverage
那个对勾都勾上了没有
六、关于单元测试(逻辑代码部分)的几点建议
不涉及
UI
方面的自动化测试,只针对逻辑代码的单元测试,下面这些建议可供参考:
- 在编码时,要尽量按照
MVVM
的模式进行开发,相比MVC
模式,MVVM
的逻辑代码都封装在VM
里面,更利于进行脱离UI
的测试。可以设想,如果将逻辑方法都写在View
或ViewController
中,则执行测试用例时就不得不引入很多额外的页面UI
组件。- 编写测试用例时,有3个核心要考虑的点,即输入,输出和结果判定。我们通过输入来设置测试用例的初始状态,通过对输出的结果判定来决定测试用例是否通过。
- 在开发中,编写的函数要尽量符合下面的特性:功能单一,有输入有输出。
- 函数有输入参数,没有返回值时,需要对输入的参数进行修改,则这种场景编写测试用例时,要判断的是执行函数操作后的原始变量是否符合预期。
- 函数没有输入参数,没有返回值时,其作用只是执行一段逻辑操作,例如存储文件,修改文件等。这时我们可以修改下功能函数,在函数内返回操作成功或失败的结果,测试用例使用此结果来作为是否通过的标准。
七、XCTest
框架中也是有 UI
测试的
例如: 创建一个 UI Test Target
自带会有这样的方法测试启动时长性能方法
生成文件示例
运行查看启动时长
启动时长日志输出
总结:默认会联系多次启动,可以查看每次的时长,方便对比结果。
遇到的 UITest
问题
testExample: Device is not configured for UI testing - use of XCUIApplication is not supported. This can happen when XCUIApplication is used in a unit test bundle instead of a UI test bundle. (NSInternalInconsistencyException)
解决方案: 1、UI test 和 逻辑 test 是不同的 target, 可能选择错了,需要创建 UI test target
更多的使用可以参考iOS单元测试的那些事儿
原博客:https://www.jianshu.com/p/c55f364b3750?v=1702025708466
标签:...,format,单元测试,iOS,a1,a2,测试,expression From: https://blog.51cto.com/u_16422529/8741267