Skip to content

[iOS][XCTest] Unity 导出工程频繁重生成时,如何稳定维护固定的 XCTest 用例 #53

Description

@nzcv

[iOS][XCTest] Unity 导出工程频繁重生成时,如何稳定维护固定的 XCTest 用例

场景:Unity(或类似工具)导出的 iOS 工程,每次重新导出 project.pbxproj 都会被重新生成,手工加进去的 XCTest target / 测试文件会丢失。而测试用例本身(.swift / .m)是稳定的。目标:用最低的维护成本,让固定的测试用例不受工程频繁重导出的影响。

问题本质

  • App 工程(pbxproj)频繁被重新生成 → 工程内手动添加的 test target、文件引用全部丢失。
  • 测试用例代码是稳定的,不应该跟着工程一起反复重建。
  • 核心诉求:把"稳定的测试"和"易变的工程"解耦

方案一(最推荐):独立的 UITest 工程,完全解耦

把测试用例放到一个单独、固定、纳入版本控制的 Xcode 工程里,不动 Unity 导出的工程。

关键点:XCUITest 不一定要编译进 App,也不需要 "Target Application"。只要用 bundle id 启动即可:

let app = XCUIApplication(bundleIdentifier: "com.yourcompany.yourgame")
app.launch()

流程:

  1. prepare() + build() 出 App
  2. 安装 App 到设备
  3. 独立测试工程xcodebuild test(或 test-without-building),针对已安装的 App

优点:

  • Unity 想怎么重导出都行,测试工程零改动、零注入逻辑。
  • 测试用例就是普通 git 仓库里的文件,维护成本最低。

代价:用 bundle-id 启动的 UITest 在录制 / 可访问性上略弱于同工程内的 UITest,但对自动化点击 / 断言完全够用。

这个方案最契合"设备抽象 + prepare/build"的架构:可在设备对象上新增一个 test() 方法(或独立 runner),指向固定测试工程即可,与 prepare / build 解耦。整条链路变成 prepare → build → install → test

方案二:测试源码外置 + 在 prepare 阶段自动注入 target

如果测试必须在同一个工程里(比如要访问 App 内部符号、做 Unit Test 而非 UITest):

  • 测试源码放在导出目录之外的固定文件夹(版本控制)。
  • 每次导出后,在 prepare()程序化地把 test target + 文件引用注入 project.pbxproj

用正则改 pbxproj 加完整 target 非常脆弱,建议用成熟库:

  • Python:pbxproj(mod-pbxproj),可 add_file / 创建 target。
  • Ruby:xcodeproj(CocoaPods 同款,最成熟)。

实现要点:注入步骤做成幂等(先检测 target 是否已存在再决定是否注入)。

方案三:预编译测试产物 + .xctestrun 复用

先编译一次测试 bundle,导出 .xctestrun,之后复用:

xcodebuild test-without-building \
  -xctestrun Tests.xctestrun \
  -destination 'platform=iOS,id=<UDID>'

适合"测试用例几乎不变、只是 App 频繁重编译"的情况。缺点:测试代码一旦改动要重新生成 xctestrun,不如方案一灵活。

推荐组合

  • 首选方案一:测试工程独立、固定,用 XCUIApplication(bundleIdentifier:) 启动已安装 App,整条链路 prepare → build → install → test,测试用例彻底脱离工程重导出的影响,维护成本最低。
  • 仅当需要 Unit Test 级别(访问 App 内部代码)时,才退而求其次用方案二的注入。
  • 方案三作为"测试稳定、只重编 App"场景下的加速手段。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions