Author fast and reliable tests for Xcode Cloud
Developer Tools 进阶 20m

为 Xcode Cloud 编写快速可靠的测试

Author fast and reliable tests for Xcode Cloud

2022年6月6日

在 Apple 官方观看视频

一句话判断

测试在 CI 中不稳定?这场 Session 给出了从模拟器配置、环境变量管理到 XCTSkip 和 XCTExpectFailure 的完整解决方案。

这场 Session 讲了什么

Xcode Cloud 的核心优势是把测试扩展到更多目标设备、更多 OS 版本、更多配置组合。但测试数量增加后,可靠性问题会指数级放大——一个偶尔失败的测试在本地可能不明显,在每天跑几十次的 CI 上就是灾难。

Session 围绕 Food Truck 示例 App,系统性地解决了四类测试可靠性问题。第一,模拟器环境假设:时区、语言、权限、预置数据这些本地开发时习以为常的设置,在 CI 的全新模拟器上可能完全不同。第二,测试前置条件处理:用 XCTSkip 优雅地跳过不满足条件的测试,而不是让它失败。第三,超时问题:用 async/await 替代 XCTestExpectation 的超时机制。第四,已知失败的管理:用 XCTExpectFailure 标记预期中的失败,避免噪声。

值得深挖的点

  • 模拟器环境假设清单:时区(服务器可能在不同时区)、Locale(数字格式和文字方向)、权限(网络、相机等需要 mock 或用 alert handler)、预置数据(推荐在 setUp 中生成 mock 数据而非依赖 tearDown)。这份清单值得每个做 CI 的团队对照检查。
  • 环境变量传递机制:Xcode Cloud 中以 TEST_RUNNER_ 为前缀的环境变量会自动传递给测试 Runner。比如 TEST_RUNNER_BASE_URL 在测试代码中变为 BASE_URL。可以在 Workflow 设置或 Test Plan 中配置。
  • XCTExpectFailure 的妙用:当某个测试依赖的外部服务(如 staging 服务器)不可用时,用 XCTExpectFailure 标记而不是跳过。测试仍然会执行,如果确实失败了会被标记为”expected failure”而不是报错;如果意外通过了,反而会提醒你可能需要移除这个标记。
  • async/await 替代超时XCTestExpectation 的超时时间很难调——太短偶尔失败,太长浪费时间。用 async/await 可以让测试自然等待,不需要猜超时值。

代码片段

// 用环境变量控制测试行为
func testOrderDonut() throws {
    let env = ProcessInfo.processInfo.environment
    let baseURL = env["BASE_URL"] ?? "https://staging.example.com"

    // 在生产环境跳过这个测试
    if env["ENVIRONMENT"] == "production" {
        XCTSkip("不在生产环境执行真实下单测试")
    }

    // 正常测试逻辑...
}

// 用 XCTExpectFailure 处理已知问题
func testStagingServiceIntegration() throws {
    XCTExpectFailure("Staging 服务器正在维护,预计会失败")

    let result = try apiClient.fetchData()
    // 如果失败 → 标记为 expected failure,不阻塞 CI
    // 如果成功 → 提示你检查是否可以移除 expectFailure
}

// 用 async/await 替代 XCTestExpectation
func testAsyncDataLoading() async throws {
    let data = try await dataSource.load()
    // 不需要设置超时,等待自然完成
    // 如果超时了,CI 的全局超时会捕获
    XCTAssertFalse(data.isEmpty)
}

// setUp 中生成 mock 数据而非依赖外部状态
override func setUp() {
    super.setUp()
    // 每次测试前生成干净的数据
    let mockMenu = generateMockMenu()
    foodTruck = FoodTruck(menu: mockMenu)
}

最佳实践

  • 所有测试状态在 setUp 中准备,不依赖 tearDown 清理
  • 用环境变量区分 CI 环境,避免在错误环境执行破坏性操作
  • 能用 async/await 就不用 XCTestExpectation
  • 预期会失败的测试用 XCTExpectFailure 而不是禁用
  • 在 Xcode Cloud 的 Workflow 中配置并行测试执行,缩短总耗时

还有什么值得关注

  • “Meet Xcode Cloud” 是入门必看
  • “Customize your advanced Xcode Cloud workflow” 讲解了 custom build scripts 的用法
  • “XCTSkip your tests” 是本 Session 的互补内容
  • 建议把测试可靠性指标纳入团队日常关注,CI 失败不应被视为”常态”
WWDC 2022