[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()
流程:
prepare() + build() 出 App
- 安装 App 到设备
- 对独立测试工程跑
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"场景下的加速手段。
[iOS][XCTest] Unity 导出工程频繁重生成时,如何稳定维护固定的 XCTest 用例
问题本质
方案一(最推荐):独立的 UITest 工程,完全解耦
把测试用例放到一个单独、固定、纳入版本控制的 Xcode 工程里,不动 Unity 导出的工程。
关键点:XCUITest 不一定要编译进 App,也不需要 "Target Application"。只要用 bundle id 启动即可:
流程:
prepare()+build()出 Appxcodebuild test(或test-without-building),针对已安装的 App优点:
代价:用 bundle-id 启动的 UITest 在录制 / 可访问性上略弱于同工程内的 UITest,但对自动化点击 / 断言完全够用。
方案二:测试源码外置 + 在 prepare 阶段自动注入 target
如果测试必须在同一个工程里(比如要访问 App 内部符号、做 Unit Test 而非 UITest):
prepare()里程序化地把 test target + 文件引用注入project.pbxproj。用正则改 pbxproj 加完整 target 非常脆弱,建议用成熟库:
pbxproj(mod-pbxproj),可add_file/ 创建 target。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,测试用例彻底脱离工程重导出的影响,维护成本最低。