生成的 Xcode 项目
- 已配置 Xcode 测试洞察(自动生成的方案包含
tuist inspect test后置操作;自定义方案需要手动添加)。
不稳定测试是指在使用相同代码多次运行时产生不同结果(通过或失败)的测试。它们会削弱对测试套件的信任,并浪费开发人员调查误报失败的时间。Tuist 会自动检测不稳定测试,并帮助你随时间跟踪它们。

不稳定检测的工作原理#
Tuist 通过两种方式检测不稳定测试:
测试重试#
当你运行带有重试功能的测试时,Tuist 会分析每次尝试的结果。如果某个测试在某些尝试中失败而在其他尝试中通过,它会被标记为不稳定。
通过 tuist test 传递 -retry-tests-on-failure 或 -test-iterations:
tuist test --scheme MyScheme -- -retry-tests-on-failure -test-iterations 3

跨运行检测#
即使没有测试重试,Tuist 也可以通过比较同一提交和同一方案在不同 CI 运行中的结果来检测不稳定测试。如果某个测试在一次 CI 运行中通过,但在同一提交和方案的另一次运行中失败,则两次运行都会被标记为不稳定。
方案是比较键的一部分,因为同一个测试在不同方案中的行为可能具有确定性差异——例如,仅针对特定模拟器目标有效的快照测试,或使用不同标志编译的测试目标。同一提交在不同方案上的两次运行被视为独立的执行变体,不会互相标记为不稳定。
这对于捕获那些未能一致失败从而无法被重试捕获,但仍会导致间歇性 CI 失败的不稳定测试特别有用。
管理不稳定测试#
自动清除#
检测和清除通过项目上的 自动化警报 运行。每个项目都有一个默认的“不稳定测试检测”自动化,其 触发器 将测试标记为不稳定,而其 恢复 会在测试在配置的恢复窗口(默认 14 天)内未重新触发后清除标记。在 设置 → 自动化 下编辑它以更改恢复窗口、交换恢复操作(例如也解除隔离),或完全禁用恢复,以便测试保持标记为不稳定直到你手动清除它们。
手动管理#
你还可以从测试用例详情页面手动标记或取消标记测试为不稳定。这在以下情况下很有用:
- 你想在修复已知不稳定测试时予以确认
- 由于基础设施问题错误地标记了测试
隔离不稳定测试#
隔离会将不稳定测试隔离开来,使其在你修复时不会阻塞 CI。默认情况下,这是一个 手动操作——你可以从仪表板中的测试用例详情页面进行隔离、解除隔离和切换模式——但你也可以将其连接到 设置 → 自动化 下的 自动化警报,以便当测试超过不稳定性阈值时自动静音(或跳过),并在恢复时解除隔离。每次转换,无论是手动还是自动,都会记录在测试用例的审计日志中。
被隔离的测试处于两种模式之一:
- 静音:测试仍然运行,但
tuist test会掩盖失败。失败仍然会馈送给不稳定测试检测器,因此你可以在不破坏构建的情况下继续观察测试。选择此模式用于你正在积极调查的测试。 - 跳过:xcodebuild 接收
-skip-testing <identifier>,因此测试永远不会启动。它不会产生新结果,并从不稳定测试仪表板中消失,直到你重新启用它。当测试损坏、缓慢或如此持续不稳定以至于运行它只是浪费 CI 分钟数时,请选择此模式。
为什么被隔离的测试可能显示为通过#
- 静音测试 仍然执行。失败的静音测试在测试用例运行中被记录为 失败 并标记为不稳定——每个测试的状态不会被重写。被覆盖的是 整体测试运行:如果运行中每个失败的测试用例都被静音,则整个运行报告为通过,因此静音失败不会破坏 CI。
- 跳过的测试 根本不运行,因此仪表板继续显示测试最后一次实际执行的状态——该快照可能已有数周之久。
运行测试#
tuist test 自动遵循这两种模式:
tuist test
跳过的测试作为 -skip-testing 传递给 xcodebuild 且永远不会启动。静音测试正常运行;如果它们失败,失败会在 resulting 构建状态中被掩盖。
要完全绕过隔离并运行所有内容,包括静音和跳过的测试:
tuist test --skip-quarantine
压力测试新测试#
压力门控会重新运行你的分支添加的测试用例,每个测试用例在全新进程中运行多次,并报告那些结果不稳定的测试。你会在引入新不稳定测试的拉取请求上看到它,而不是几周后。
当测试用例在过去 90 天内未在项目的默认分支上运行,或在已合并到该分支的提交上未运行时,它被视为新测试用例。使用合并队列时,队列运行的测试用例在队列的提交合并后即被知晓,如果队列丢弃它,则保持为新测试用例。提交通过 Tuist 在 CI 上收集的 Git 历史 进行匹配。
启用它#
门控默认关闭。设置一种模式:
report:警告每个不稳定测试用例。运行仍以其自身结果退出。enforce:不稳定的测试用例会使得运行失败,其退出代码与失败的测试相同。
在这两种模式下,选择性测试 不会在下次运行时跳过包含不稳定新测试用例的测试目标。该目标会再次运行,门控会再次对该测试用例进行压力测试,直到它通过所有重新运行。
先从 report 开始使用几周,看看 enforce 会阻止哪些内容。
将其放在透传参数之前传递,或设置 TUIST_TEST_STRESS_NEW_TESTS 以根据 CI 通道调整模式:
tuist test --stress-new-tests report
每个测试重新运行的次数#
这取决于测试用例在首次运行中所花费的时间:
| 首次运行 | 重新运行 |
|---|---|
| 最多 5 秒 | 10 |
| 最多 10 秒 | 5 |
| 最多 30 秒 | 3 |
| 最多 5 分钟 | 2 |
| 超过 5 分钟 | 不重新运行 |
重新运行会复用首次运行构建的内容。运行会在达到 200 个测试用例或 10 分钟时停止(以先到达者为准),并说明触及了哪个限制。
门控不运行的情况#
- 首次运行已经失败。请先修复这些测试。
- 项目没有默认分支,或者尚未在其上运行任何 CI。
- 超过 30% 的项目测试用例看起来是新的。重新运行这么多测试用例所需的时间可能得不偿失。在刚开始向 Tuist 报告的项目中,或在重命名导致大量测试同时移动后,可能会出现这种情况。
Slack 通知#
通过在 Slack 集成中设置 不稳定测试警报,在测试变得不稳定时立即收到通知。
查询不稳定和隔离状态#
CLI#
tuist test case 命令树公开了 Tuist 跟踪的关于测试用例的所有信息:
tuist test case list --flaky # only flaky test cases
tuist test case list --quarantined # only muted or skipped test cases
tuist test case show <test_case_id> # detail: flakiness rate, last status, run counts
tuist test case events <test_case_id> # audit log: marked_flaky, muted, skipped, ...
tuist test case run list <test_case_id> # run history with status and duration
tuist test case run show <test_case_run_id> # single run, including failure breakdown
所有这些命令都接受 --json 以用于脚本编写。
REST API#
相同的数据可通过 HTTP 获取——请参阅 API 参考中的 测试用例端点 以获取完整的路由、过滤器和响应字段列表。要更改测试用例的状态(标记或取消标记为不稳定、静音、跳过),请使用 is_flaky 或 state 发送 PATCH /api/projects/{account_handle}/{project_handle}/tests/test-cases/{test_case_id}。相同的更改也可通过 tuist test case update 以及 update_test_case Model Context Protocol 工具实现。