跳转到内容
Skip
3.2k

测试

Skip 只需一条命令,就能在两个平台上构建并运行 Swift 测试,帮助你确认代码在 Apple 平台和 Android 上的行为是否一致。核心命令是 skip test,它同时适用于 Skip Lite(将 Swift 转译为 Kotlin)和 Skip Fuse(将 Swift 原生编译到 Android)模块。

Testing Diagram

在包目录中运行:

Terminal window
skip test

Skip 会同时在两端运行测试集:一端是主机上的原生环境(通常是 Mac,报告中显示为 Darwin (macOS);也支持 Linux,例如 CI 环境),另一端是 Android,然后并排输出两端的一致性报告:

| Test | Case | Darwin (macOS) | Android (Robolectric) |
| ---------- | ------------ | -------------- | --------------------- |
| MathTests | testAdd() | PASS (0.00s) | PASS (0.01s) |
| MathTests | testDivide() | PASS (0.01s) | PASS (0.02s) |
| | | 100% | 100% |

每个测试都会按平台匹配,并显示结果与运行时间。因此,像“Darwin 通过、Android 失败”这类可移植性问题会非常醒目。

同一条 skip test 命令适用于所有模块模式:

  • Skip Lite 模块会把 XCTest 和 Swift Testing 用例转译为 Kotlin/JUnit,并在 JVM 上运行。
  • Skip Fuse 模块(包括 mode: native 模块)会将测试用例原生编译到 Android 并直接运行,底层仍由同一套 Gradle 测试流程驱动。

在 Xcode 中,可以通过标准的测试操作运行同一批测试(底层会调用 swift test),因此结果会像普通 XCTest 结果一样出现在测试导航器和 CI 中。

默认情况下,Android 端使用 Robolectric 在主机本地运行。Robolectric 会在主机 JVM 上模拟 Android 环境,无需模拟器或真机,所以速度最快,适合作为日常开发的默认选择。Robolectric 提供了许多框架 API(如 ContextSharedPreferences 和资源系统),足以覆盖绝大多数单元测试。

如果需要更高的真实度,设置 ANDROID_SERIAL 后即可在已连接的模拟器或真机上运行:

Terminal window
ANDROID_SERIAL=emulator-5554 skip test

此时 Skip 会在真实 Android 运行时中以仪器化测试的形式运行测试,可使用完整的 Android 框架。它比 Robolectric 慢,但最接近生产环境的实际行为。可用 adb devices 列出设备 ID,也可以在 Xcode scheme 的 Run 操作中设置 ANDROID_SERIAL

Configuring running tests on emulator in Xcode

Robolectric 与 Android 很接近,但并不完全相同。尤其要注意:由于代码运行在主机 JVM 上,Robolectric 环境中的 #if os(Android)false。Skip 另外定义了 ROBOLECTRIC 符号,让你能在所有类 Android 环境中走 Android 分支:

#if os(Android) || ROBOLECTRIC
// 在真机、模拟器和 Robolectric 下都会执行
#endif

可以使用哪些测试框架,取决于模块模式:

  • Skip Lite 模块同时支持 XCTestSwift Testing,两者都会随其他代码一起转译为 Kotlin/JUnit。
  • Skip Fuse 原生模块仅支持 Swift Testing,它们会直接运行原生 Swift Testing 运行时(通过 swt 入口),不会执行 XCTest 用例。

如果你的代码已经是(或未来可能成为)原生 Fuse 模块,请使用 Swift Testing 编写测试。

  • XCTest — 使用 XCTestCase 子类和以 test 开头的方法;XCTAssert* 函数会映射到对应的 Android 断言。(仅 Skip Lite)
  • Swift Testing — 使用 @Test 函数、@Suite 类型,以及 #expect#require 宏。独立的 @Test 函数(未嵌套在类型中)会被自动包装。

对 Skip Lite 模块而言,转译后的测试用例与其他 Lite 代码遵循相同规则;一些 Swift Testing 特性(参数化测试、trait 和 tag)尚不支持转译。Skip Fuse 原生模块运行真正的 Swift Testing 运行时,因此不受这些限制。

Android 端的测试由自动生成的驱动器 XCSkipTests.swift 负责。通常你不需要碰它,Skip 构建插件会在构建时自动生成。如果需要自定义(例如更换 Gradle 任务),可以在测试目标中添加自己的实现,插件将优先使用它:

#if os(macOS) // Skip 转译测试仅在 macOS 目标上运行
import SkipTest
@available(macOS 13, *)
final class XCSkipTests: XCTestCase, XCGradleHarness {
public func testSkipModule() async throws {
try await runGradleTests()
}
}
#endif

这个驱动器负责将 Xcode 和 swift test 连接到 Gradle 流程:运行它时,会将测试转译或编译到 Android,执行测试,再把结果以 XCTest 结果的形式回传。

Robolectric(默认)模拟器 / 真机
命令skip testANDROID_SERIAL=… skip test
运行位置主机 JVM已连接的 Android 模拟器/真机
Android API由 Robolectric 模拟完整可用
速度最快较慢
真实度适合单元测试最高

Skip Lite 和 Skip Fuse 模块都使用这套流程。日常开发中,Robolectric 能提供最快的反馈循环;需要验证依赖真实 Android 运行时的行为时,再切换到模拟器或真机。

如需参考在 GitHub CI 中使用 Android 模拟器运行 Skip Fuse 测试的项目,请查看 skip-fuse-samples

对于可同时编译到 iOS 和 Android、但没有 skip.yml 的原生 Swift 包(例如 swiftpackageindex.com 跟踪 Android 兼容性的数千个第三方包),请参阅移植指南中的测试说明。

Android 设备上 Debug 与 Release 构建的性能往往差异很大。测试真实性能时,务必使用 Release 构建在真机上运行

使用 skip android test 直接在设备上测试

Section titled “使用 skip android test 直接在设备上测试”

skip android test 会交叉编译包的测试目标,并不经过 Gradle,直接在真机或模拟器上运行。它有两种模式:

  • 命令行模式(默认)— skip android test — 把测试目标交叉编译为可执行文件,使用 adb push 将二进制文件、依赖库和所有 .resources 目录复制到设备,再通过 adb shell 运行,就像 Linux 上的命令行程序。由于 .resources 与二进制文件位于同一层,Bundle.module 资源查找可正常工作;但此环境没有 JVM 或 JNI,因此无法使用 Android 框架 API。
  • APK 模式skip android test --apk — 把测试打包成真正的 Android 应用(由 aapt2zipalignapksigner 组装),通过 adb install 安装,并以 Android Activity 的形式运行。这种模式提供完整的 JNI 环境和全部 Android 框架能力,但无法解析从 APK 原生库目录加载的资源。可传入 --event-stream-output-path <file>,在本地捕获原始测试事件 JSON。