探究 Xcode 命令行用法二:xcodebuild 测试实践

本文还是 adat 项目的延伸,瞄准开发周期的测试环节,主要介绍 Xcode 范畴内的测试概念和自动化测试实践,不会讨论如何编写高质量的测试用例。文中涉及到 Xcode 构建的许多概念,大部分已在前两篇文章中有所说明,建议先大致浏览一遍。



内容概览

  • Xcode 测试相关的概念
  • xcodebuild 命令中测试相关的用法
  • xcodebuild 测试实践
  • 存在的问题


Xcode 测试相关的概念

在开始测试之前,有必要简单介绍一下 Xcode 中测试相关的概念,这对于编写自动化测试命令非常重要。因为文中会反复提到这些概念,使用时也必须清楚这些概念之间的关系。


测试包

Test Bundle,专门用于测试的 Target。Xcode 13.1 新建项目时勾选 Include Tests 会自动创建单元测试包和 UI 测试包。测试包内组织了多个测试用例。


测试用例

Test Case,专门用于测试的类 Class,继承自 XCTestCase。在测试用例内导入需要测试的头文件,设置初始环境,并编写多个测试方法。


测试方法

Test Method,专门用于测试的方法,必须是实例方法且方法名必须以 test 开头且没有参数和返回值,否则不会被 Xcode 识别为测试方法,也就不会被自动调用。


单元测试

Unit Test,是一种测试维度,主要测试代码逻辑,粒度较细,依赖良好的架构设计并编写可测试的代码(Testable Code)。在测试方法中调用需要测试的代码,并用 XCTAssert 及相关方法来判断结果。


UI 测试

UI Test,在较粗维度上进行测试,模拟用户的操作。Xcode 用 UI Test Recorder 来记录操作序列,自动将序列以代码的形式插入到测试方法中,运行测试方法就是把序列“重放”一次,以观察同样的操作在不同设备环境上的表现。UI 测试可以在需要的位置进行截屏,保留现场。


测试计划

Test Plan,一个以 .xctestplan 为扩展名的 JSON 格式文件,组合了测试包和配置(Configuration),可以分别设置测试包、测试用例、测试方法是否启用以决定是否参与测试,配置包含一个默认配置和多个自定义配置,大部分配置项来自于 Scheme。自定义配置未指定的配置项由默认配置的对应配置项决定。

在 Scheme Manager 中,可以将 Scheme 转为测试计划,也可以在 Xcode -> Product -> Test Plan 创建新的测试计划。对于大型项目,建议将 Scheme 转为测试计划,因为测试计划包含更多配置项,比如启用测试超时、重复测试等,而且 JSON 文件比 Scheme 更易于版本管理。

必须要为测试计划创建至少一个自定义配置,否则测试计划内的任何测试都将无法运行。在将 Scheme 转为测试计划时,Xcode 帮我们自动完成了这个操作。你也可以添加额外的自定义配置。

在这里插入图片描述
在这里插入图片描述

测试报告

Test Reports,列出了每个测试用例的测试结果:执行步骤、耗时、截屏、日志等。
在这里插入图片描述

代码覆盖率

Code Coverage,显示测试用例覆盖了多少代码,Xcode 提供了可视化界面,可据此来完善测试用例。启用代码覆盖率并执行测试后,代码编辑器右边缘就会显示某行被测试的次数。
在这里插入图片描述



xcodebuild 命令中测试相关的用法

了解完测试相关的概念,结合 探究 Xcode 命令行用法一:Xcode 构建必备认知 一文对命令行的认知,就可以对 xcodebuild 测试命令进行分析了。在 Terminal 中执行man xcodebuild提取测试相关的选项,对每个选项和操作的用法说明如下:


多环境并行测试

-disable-concurrent-destination-testing
禁止在多个环境中并行测试。如果指定了多个环境,一个完成后再开始下一个。

-maximum-concurrent-test-device-destinations NUMBER
测试时如果指定了多个真机环境,同一时间最多在 NUMBER 个真机上执行。和-disable-concurrent-destination-testing互斥。如果未明确指定 NUMBER,理论上没有上限。

-maximum-concurrent-test-simulator-destinations NUMBER
测试时如果指定了多个模拟器环境,同一时间最多在 NUMBER 个模拟器上执行。和-disable-concurrent-destination-testing互斥。如果未明确指定 NUMBER,默认为 4。


多运行器平行测试

-parallel-testing-enabled YES|NO
是否启用平行测试。平行测试将测试用例(类)分发到不同的运行器(Runner)或进程中执行,以提高测试效率。单元测试的运行器通常是应用的一个实例,UI 测试的运行器是 Xcode 创建的一个自定义应用。

-parallel-testing-worker-count NUMBER
执行平行测试的运行器数量,会覆盖-maximum-parallel-testing-workers NUMBER指定的数量。

-maximum-parallel-testing-workers NUMBER
执行平行测试的运行器的最大数量。

-parallelize-tests-among-destinations
将平行测试分发到多环境中执行。如果启用了平行测试并指定在多个环境中测试,那么测试用例会被分发到多环境中执行,而不会将一套完整的测试在每一个环境中执行。


测试计划

-showTestPlans
显示 Scheme 关联的测试计划,需要和 -scheme 搭配使用。

-testPlan
执行测试时指定测试计划,只传测试计划文件名,不传 .xctestplan 后缀。需要和 -scheme 搭配使用。

-only-test-configuration
只测试指定的配置,参数为自定义配置名称,区分大小写。注意这是测试计划的配置,不是 Target 的 BuildSettings 的变体。

-skip-test-configuration
跳过对指定自定义配置的测试。


测试超时

-test-timeouts-enabled YES|NO
是否启用测试超时。

-default-test-execution-time-allowance SECONDS
一个测试方法的默认超时时间,单位秒,每 60 秒向上取整,即:小于 60 秒按 60 秒算,大于 60 秒但小于 120 秒按 120 秒算,以此类推,例如:SECONDS 等于 59,实际是 60;SECONDS 等于 61,实际是 120 。需要先启用测试超时:-test-timeouts-enabled YES。等同于 XCTestCase 实例的 executionTimeAllowance 的作用,另外测试计划中也可以配置默认超时时间。优先级从高到低依次是:

  1. XCTestCase 的 executionTimeAllowance 属性值
  2. xcodebuild 的 -default-test-execution-time-allowance 选项的参数值
  3. 测试计划的 Default Test Execution Time Allowance (s) 配置项的值
  4. 未指定则为默认值 600 秒。

-maximum-test-execution-time-allowance SECONDS
一个测试方法的最大超时时间,单位秒,每 60 秒向上取整。需要先启用测试超时:-test-timeouts-enabled YES。XCTestCase 实例并未提供相关属性或方法去设置,也没有默认最大超时时间,因此优先级从高到低依次是:

  1. xcodebuild 的 -maximum-test-execution-time-allowance 选项的参数值
  2. 测试计划的 Maximum Test Execution Time Allowance (s) 配置项的值

一个测试方法的超时时间由默认超时时间和最大超时时间的较小者决定。达到超时时间后,判定测试失败,并生成一个 Spindump 文件:
在这里插入图片描述
后续会有文章介绍如何解析 Spindump 文件。关注我,第一时间获取:virusbee - 本文作者


重复测试

-test-iterations <number>
重复测试<number>次。

-retry-tests-on-failure
测试失败后再次尝试,直到测试成功或达到总测试次数。总测试次数默认是 3 次,如果指定了-test-iterations <number>,则总次数为<number>次。不能和-run-tests-until-failure一起用。

-run-tests-until-failure
测试成功后再次运行,直到测试失败或达到总测试次数。总测试次数默认是 100 次,如果指定了-test-iterations <number>,则总次数为<number>次。不能和-retry-tests-on-failure一起用。

-test-repetition-relaunch-enabled YES|NO
是否每个测试都在新进程中执行,如果为 NO,所有的测试都将在同一个进程中执行。必须和-test-iterations <number>-retry-tests-on-failure-run-tests-until-failure搭配使用。


本地化测试

-testLanguage
指定测试语言,语言遵循 ISO 639-1 标准,例如 nl、hr、ar。

-testRegion
指定测试地区,地区遵循 ISO 3166-1 标准,例如 GN、KE、IT。


代码覆盖率

-enableCodeCoverage YES|NO
启用代码覆盖率


筛选测试用例

-only-testing:TEST-IDENTIFIER
只测试 TEST-IDENTIFIER 指定的测试,关于 TEST-IDENTIFIER 的详细说明,参见 TN2339: 使用 Xcode 命令行构建的常见问题如何利用命令行实施单元测试?

-skip-testing:TEST-IDENTIFIER
跳过 TEST-IDENTIFIER 指定的测试。


测试操作

test
执行测试。

build-for-testing
生成 .xctestrun 文件,作为test-without-building的参数。

test-without-building
利用build-for-testing生成的 .xctestrun 文件进行测试,通过-xctestrun选项来指定。

关于测试操作的详细用法,参见 TN2339: 使用 Xcode 命令行构建的常见问题如何利用命令行实施单元测试?如何利用命令行实现 Xcode 中的 Build For Testing 和 Test Without Building 功能?



xcodebuild 测试实践

测试需求

项目概况
一款 iOS 应用,最低支持 iOS 9.0;应用发行到香港、日本、韩国 3 个地区;支持简体中文、繁体中文、日语、韩语、英语 5 种语言;已有若干单元测试和 UI 测试用例,部分测试用例已整合为测试计划。

项目清单
项目路径:App.xcodeproj
Targets:App(主应用)、AppTests(单元测试包)、AppUITests(UI 测试包)
Scheme:App(配置测试计划)、AppTests(配置单元测试包和UI 测试包)
发行地区:香港 HK、日本 JP、韩国 KR
支持语言:简体中文 zh-Hans、繁体中文 zh-Hant、日语 ja、韩语 ko、英语 en
测试计划:Core.xctestplan(只有一个默认配置和一个自定义配置,配置项均为默认值)
单元测试包内的用例(类):AccountTests,DataTests,ParseTests,HelperTests(方法 testExample 无需测试)
UI 测试包内的用例(类):LoginTests,ChatTests,PaymentTests

现有设备

类型名称或IDOS
真机ID:4cbbc9c59cd4c29dad494141b81b2f64ab45d643iOS 15.1
真机ID:00004032-102044371DF4313AiOS 14.5.1
模拟器名称:iPhone 13 ProiOS 15.0
模拟器名称:iPhone SEiOS 13.5
模拟器名称:iPad ProiOS 12.4

测试要求
测试计划需要在每台设备上完整执行 10 次测试,直到失败为止,每个测试限制在 1 分钟内完成,模拟器最多同时开 2 个;其余测试用例采用平行测试,测试失败不停止,最多测试 10 次;输出代码覆盖率、测试报告、日志和失败截图。


需求分析及实现

根据测试要求,测试计划和其余测试用例分开执行,需要写两条命令。

对于测试计划Core.xctestplan,用-testPlan Core来指定;测试计划需搭配 Scheme 使用,而该测试计划配置在 App Scheme 下,即-scheme App;执行 10 次,即-test-iterations 10;直到失败为止,即-run-tests-until-failure;为了确保每次都在新进程中测试,添加-test-repetition-relaunch-enabled YES;每个测试限制在 1 分钟内完成,需要启用测试超时,即-test-timeouts-enabled YES,并将超时时间设置为 60 秒,即-maximum-test-execution-time-allowance 60;上表列出的每台设备都要参与测试,根据各自类型、名称、ID、版本来构造 -destination 参数值,有几台设备就写几个-destination;模拟器最多同时开 2 个,由于模拟器默认最多同时开 4 个,因此设置-maximum-concurrent-test-simulator-destinations 2;输出代码覆盖率,即启用代码覆盖率-enableCodeCoverage YES;为了方便获取代码覆盖率、测试报告等,将测试结果输出到指定的位置result1,即-resultBundlePath result1

完整命令:

$ xcodebuild test \
-project App.xcodeproj \
-scheme App \
-testPlan Core \
-test-iterations 10 \
-run-tests-until-failure \
-test-repetition-relaunch-enabled YES \
-test-timeouts-enabled YES \
-maximum-test-execution-time-allowance 60 \
-destination "platform=iOS,id=4cbbc9c59cd4c29dad494141b81b2f64ab45d643" \
-destination "platform=iOS,id=00004032-102044371DF4313A" \
-destination "platform=iOS Simulator,name=iPhone 13 Pro,OS=15.0" \
-destination "platform=iOS Simulator,name=iPhone SE,OS=13.5" \
-destination "platform=iOS Simulator,name=iPad Pro,OS=12.4" \
-maximum-concurrent-test-simulator-destinations 2 \
-enableCodeCoverage YES \
-quiet \
-resultBundlePath result1

思考一下,这个命令执行完成后,所有测试通过,一个普通的测试方法执行了几次?答案是 50 次。这是一个并行测试,因此测试计划会在每台设备上完整执行 10 次,有 5 台设备参与测试,测试计划只有 1 个自定义配置,即:10次 * 5设备 * 1配置 = 50 次。

对于其余测试用例,单元测试用例属于 AppTests 包,UI 测试用例属于 AppUITests 包,且两个包都配置在 AppTests Scheme 下,因此指定-scheme AppTests; AppTests 包内的 HelperTests 类的 testExample 方法无需测试,即-skip-testing:AppTests/HelperTests/testExample;最多测试 10 次,即-test-iterations 10;测试失败不停止,即-retry-tests-on-failure;采用平行测试,需先启用平行测试,即-parallel-testing-enabled YES,再将测试用例分到-destination中执行,即-parallelize-tests-among-destinations;其余配置和测试计划保持一致。

完整命令:

$ xcodebuild test \
-project App.xcodeproj \
-scheme AppTests \
-skip-testing:AppTests/HelperTests/testExample \
-test-iterations 10 \
-retry-tests-on-failure \
-destination "platform=iOS,id=4cbbc9c59cd4c29dad494141b81b2f64ab45d643" \
-destination "platform=iOS,id=00004032-102044371DF4313A" \
-destination "platform=iOS Simulator,name=iPhone 13 Pro,OS=15.0" \
-destination "platform=iOS Simulator,name=iPhone SE,OS=13.5" \
-destination "platform=iOS Simulator,name=iPad Pro,OS=12.4" \
-parallel-testing-enabled YES \
-parallelize-tests-among-destinations \
-enableCodeCoverage YES \
-quiet \
-resultBundlePath result2

思考一下,这个命令执行完成后,所有测试通过,一个普通的测试方法执行了几次?答案是 1 次。虽然最多执行 10 次,但如果第一次就执行成功,就不会再执行了,这是因为 -retry-tests-on-failure 只决定了测试失败的后续操作,而 -run-tests-until-failure 才是决定测试成功的后续操作,此处并未指定它,也无法指定它,两者是相互排斥的。如果第一次就执行成功,导致所有方法只测试了一次,那这个测试不充分,而测试需求就是这样要求,可以反推这个需求提的并不合理。


测试结果

测试结果保存在-resultBundlePath指定的路径,不能是已存在的路径。上述测试计划的测试结果是 result1.xcresult,其余测试用例的测试结果是 result2.xcresult。

双击 result1.xcresult 自动在 Xcode 中打开,查看测试结果:
在这里插入图片描述

代码覆盖率:
在这里插入图片描述



存在的问题

本地化

上面的命令还不够完善,无法支持多个地区和语言的测试要求,某些情况还要模拟地理位置,这应该如何处理?


将测试委托给第三方

如果测试任务繁重,又没有很多测试设备可用,往往需要将测试任务委托给第三方来完成,但又不能提供源码,这种情况该如何处理?


与持续构建系统结合

很多企业自己有持续构建系统,项目构建完成后会自动发邮件给关注者。直接把 .xcresult 文件通过邮件发送,是很不明智的做法,那如何自己解析 .xcresult 文件获取元数据,并生成自定义的测试报告、代码覆盖率界面呢?

限于篇幅,此处不展开说明,后续会有文章提供解决方案,关注作者第一时间获取。



精彩预告

本文概念非常多,实践部分也需要结合概念去写代码一步步验证,如果不太好理解,建议先收藏下来,慢慢消化。

下面是后续文章的计划(标题和顺序可能变动,以实际发布为准):

  • 探究 Xcode 命令行用法三:xcodebuild 打包
  • 探究 Xcode 命令行用法四:codesign 签名
  • 探究 Xcode 命令行用法五:上传与分发
  • 探究 Xcode 命令行用法六:Jenkins 持续构建
  • 探究 Xcode 命令行用法七:xcodebuild 测试相关问题解决方案
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 要重新安装 Xcode 命令行工具,请执行以下步骤: 1. 打开终端。 2. 输入 xcode-select --install 并回车。 3. 点击 "安装" 按钮。 4. 等待安装完成。 5. 完成后,在终端中输入 xcode-select -p 并回车,检查安装路径是否正确。 请注意,您可能需要管理员权限才能完成此操作。 ### 回答2: 重新安装Xcode命令行工具的步骤如下: 1. 打开终端应用程序,在终端中输入以下命令:xcode-select --print-path。这将显示当前安装的Xcode工具的路径。 2. 如果命令返回一个路径,这意味着已经安装了Xcode命令行工具。可以跳过下一步。如果命令没有返回路径,说明尚未安装Xcode命令行工具,可以继续下一步。 3. 打开终端,输入以下命令来安装Xcode命令行工具:xcode-select --install。这将弹出一个对话框,询问是否要下载和安装Xcode命令行工具。 4. 点击“安装”按钮,等待下载和安装过程完成。这可能需要一些时间,取决于网络连接的速度。 5. 安装完成后,终端将显示一条消息确认Xcode命令行工具已成功安装。 6. 验证安装是否成功,可以在终端中键入以下命令:xcode-select --print-path。此时终端应该返回新安装的Xcode命令行工具的路径。 重新安装Xcode命令行工具可以解决一些与开发和编译相关的问题。如果正在进行软件开发工作或使用与Xcode相关的工具,重新安装Xcode命令行工具可能是一个好的解决方法。 ### 回答3: 要重新安装Xcode命令行工具,可以按照以下步骤进行: 1. 打开终端应用程序,可以在“应用程序”文件夹中找到它。 2. 在终端窗口中输入以下命令,按下回车键,卸载现有的Xcode命令行工具: ``` sudo rm -rf /Library/Developer/CommandLineTools ``` 此命令将删除已安装的Xcode命令行工具。 3. 继续在终端中输入以下命令,按下回车键,安装Xcode命令行工具: ``` xcode-select --install ``` 运行此命令会弹出一个窗口,询问是否安装Xcode命令行工具,点击“安装”按钮。 4. 等待安装完成。安装过程可能需要一些时间,取决于网络速度和计算机性能。 5. 安装完成后,可以通过输入以下命令检查Xcode命令行工具是否正常安装: ``` xcode-select -p ``` 如果显示类似`/Library/Developer/CommandLineTools`的路径,说明Xcode命令行工具已成功安装。 通过以上步骤,你就可以重新安装Xcode命令行工具了。记得在执行命令时提供管理员权限(sudo)以完成卸载和安装过程。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值