Golden Test — 什么是黄金测试,快照测试如何工作及应用

作者: IT Sectr 发布日期: 2026-04-10 阅读时间: 9 分钟

Golden Test(快照测试,基准测试)— 是一种 UI 可视化测试方法,将组件的当前渲染与预先保存的基准图像(golden 文件)进行比较。如果像素变化超过设定的阈值,测试失败并生成差异图像。开发人员查看差异并接受更改(更新 golden)或修复错误。更多信息请参阅 Meta Engineering 关于 Paparazzi 的文章

要点

  • Golden Test — 将当前 UI 与基准图像进行比较以检测视觉回归
  • 差异图像 — 不匹配时,golden 测试生成带有高亮更改像素的差异图
  • Android — Paparazzi 和 Roborazzi 用于 compose 和 view 组件的截图测试
  • iOS — SwiftSnapshotTesting(pointfree.co)和 Uber 的 iOSSnapshotTestCase 用于 SwiftUI 和 UIKit
  • CI 集成 — golden 测试在 CI 上运行,并在 UI 意外更改时失败

什么是 Golden Test 及其工作原理?

Golden Test — 是一种通过逐像素与基准比较来自动检查组件外观的方法。过程:(1)开发人员或测试人员拍摄组件的第一个截图 — 这就是 “golden”(基准)。(2)Golden 文件保存在测试旁边的仓库中。(3)在后续运行中,测试重新渲染组件并与保存的 golden 进行比较。(4)如果图像匹配 — 测试通过。如果不同 — 测试失败并显示差异图。决定:要么更改是预期的(我们更新 golden),要么是错误。

如何生成 golden — 库在离屏缓冲区中渲染组件(Android:Canvas,iOS:UIGraphicsImageRenderer),无需真实显示器。这意味着 golden 测试在 CI 上无需屏幕模拟器(虚拟显示)即可运行,从而加快执行速度。Android 上的 Paparazzi 使用 Android Studio 的 Layoutlib — 与布局编辑器相同的引擎。iOSSnapshotTestCase 使用 UIKit 渲染到 CGImage。结果 — 固定大小的 PNG 文件。

Golden 文件大小和存储管理

Golden 文件 — 一个屏幕的 PNG 截图(1080x1920)根据复杂程度占用 200-800 KB。对于有 500 个 golden 测试的项目,仓库中约为 100-400 MB。解决方案:(1)将 golden 存储在 Git LFS 中。(2)使用 PNG 压缩(pngcrush,oxipng)。(3)将 golden 存储在单独的存储(S3)中,并在构建时拉取。在 IT Sectr,我们将 golden 存储在 Git LFS 中,每个文件限制为 1 MB — 这对于 90% 的测试来说足够了。

Flaky golden 测试及其解决方案

Flaky golden 测试 — golden 测试的主要问题。不同的 GPU、字体版本和抗锯齿会导致像素上的微小差异。解决方案:阈值(允许的不同像素百分比)、模糊比较以及在相同的 CI 代理上运行(相同的 GPU、操作系统、模拟器版本)。Paparazzi 使用逐像素完美比较,因此 CI 代理必须相同。

Golden Test 与 Screenshot Test:有什么区别?

Golden Test — 是一种具有固定基准的截图测试。术语 “golden” 意味着基准已经过团队批准(接受)并存储在仓库中。每次图像更改都需要开发人员有意识地决定:更新 golden 或修复代码。Golden Test 在单个组件(Composable,UIView)级别工作,不需要真实设备。

Screenshot Test — 是一个更广泛的概念。截图测试可以捕获整个屏幕,包括真实数据、导航、系统状态栏和动画。截图测试通常通过 UI Automator(Android)或 XCUITest(iOS)在真实设备或模拟器上运行。Golden 测试在单元测试环境(JVM,XCTest)中无需模拟器即可运行,并且只捕获单个组件。

特性Golden TestScreenshot Test
级别组件/Composable/View整个屏幕
环境单元测试(离屏缓冲区)设备/模拟器
速度每个测试 50-200 毫秒每个测试 2-30 秒
动画不支持支持(带暂停)
无 GPU 的 CI可工作(Layoutlib)需要模拟器
配置复杂性高(模拟器/设备农场)
Flakiness中等(不同的 GPU)高(模拟器,时间)

覆盖策略:golden 与 screenshot

Golden 与 Screenshot — golden 测试用于在每次提交时检查单个 UI 组件(按钮、卡片、对话框)。截图测试 — 用于在发布前对整个屏幕进行端到端检查。Golden 测试为开发人员提供快速反馈,截图测试 — 确保整个应用程序的完整性。在 IT Sectr,我们对拉取请求使用 golden 测试(3-5 分钟),对每晚构建使用截图测试(30-60 分钟)。

Paparazzi 和 Roborazzi:Android 上的快照测试

Paparazzi — 来自 Cash App(Square)的库,无需模拟器即可将 Android View 和 Jetpack Compose 组件渲染为 PNG。使用 Layoutlib(与 Android Studio 预览相同的引擎)。设置:连接 Gradle 插件,使用 @Test 和 @RunWith(PaparazziRule::class) 编写测试,调用 paparazzi.snapshot(view)。Paparazzi 不支持动画、视频和真实设备 — 仅支持组件的静态渲染。

kotlin
// build.gradle.kts (module)
plugins {
    id("app.cash.paparazzi") version "1.3.1"
}

// Compose 组件的 Golden test
class ButtonGoldenTest {

    @get:Rule
    val paparazzi = Paparazzi(
        Paparazzi.PaparazziSnapshotConfig(
            deviceConfig = DeviceConfig.PIXEL_6,
            theme = "android:Theme.Material.Light.NoActionBar"
        )
    )

    @Test
    fun primary_button() {
        paparazzi.snapshot {
            Button(
                onClick = { },
                modifier = Modifier.width(200.dp)
            ) {
                Text("Submit")
            }
        }
    }
}

Roborazzi — Paparazzi 的替代方案,支持 Compose、View 和图像比较。区别:Roborazzi 通过 Robolectric 工作并支持阈值(允许的像素差异百分比)。这减少了 CI 上不同 GPU 的不稳定性。Roborazzi 还可以创建更改的 GIF 动画(之前/之后/差异),方便代码审查。Golden 文件格式:PNG + JSON 元数据。

更新 golden — 在有意的 UI 更改后,开发人员删除旧的 golden 文件并使用记录标志运行测试。Paparazzi 重新创建所有 golden 文件。然后开发人员将新的 golden 与代码更改一起提交。在代码审查中,审查者可以看到新旧 golden 的差异。如果更改获得批准 — PR 合并。如果没有 — 开发人员修复代码并重新运行测试。切勿在 CI 上自动更新 golden — 仅在本地进行。

iOS 上的 SwiftSnapshotTesting 和 iOSSnapshotTestCase

SwiftSnapshotTesting — 来自 pointfree.co 的库,Composable Architecture 的创建者。支持 UIView、UIViewController、CALayer 和 SwiftUI View。原理:assertSnapshot(matching: view, as: .image)。首次运行时,golden 会自动创建。后续运行时 — 进行比较。如果差异超过允许范围 — 测试失败。SwiftSnapshotTesting 通过 UIGraphicsImageRenderer 工作,与 CI 兼容(Xcode Cloud,GitHub Actions)。

swift
import SnapshotTesting
import XCTest

final class ProfileCardSnapshotTests: XCTestCase {

    func test_profile_card_default() {
        let card = ProfileCard(
            name: "Alice",
            avatar: UIImage.testImage(),
            badge: "Pro"
        )
        let controller = UIHostingController(rootView: card)

        assertSnapshot(
            matching: controller,
            as: .image(on: .iPhoneSe),
            record: ProcessInfo.processInfo
                .environment["RECORD"] != nil
        )
    }
}

iOSSnapshotTestCase(原名 FBSnapshotTestCase)— 来自 Uber 的 UIKit 库。与 SwiftSnapshotTesting 不同,iOSSnapshotTestCase 需要指定屏幕大小和方向。Golden 文件 — ReferenceImages 文件夹中的 PNG。优点:无需 SwiftUI 即可与 UIKit 配合使用,并支持 iOS 12+。缺点:不会自动更新 golden — 需要使用记录标志运行。SwiftSnapshotTesting 更现代,推荐用于新项目。

特定设备的 golden — golden 文件因不同的屏幕尺寸和方向而异。标准方法:将 golden 命名为 TestName@3x~iPhone14.png。如果指定了 .image(on: .iPhoneSe) 参数,SwiftSnapshotTesting 会自动添加设备后缀。在 Android 上,Paparazzi 使用 DeviceConfig 来设置大小。为每个支持的设备外形因素单独存储 golden。不要为不同的尺寸使用同一个 golden — 这会导致不稳定的测试。

在 CI 中处理 golden 文件和管理更新

CI 管道 — golden 测试应在每个拉取请求上运行。如果测试失败,CI 会将差异图像显示为构建产物。开发人员查看差异并做出决定。重要提示:CI 上生成的 golden 文件永远不会自动提交。只有在有意更改后由开发人员本地生成。GitHub Actions 和 GitLab CI 支持上传产物(png,html),以便在浏览器中查看差异。

仓库大小 — golden 文件增长迅速。500 个测试 = 100-400 MB PNG。解决方案:(1)Git LFS — 每个 golden 都存储在 LFS 中,仅在检出时克隆。(2)将 golden 存储在单独的仓库中并作为子模块连接。(3)S3 + 缓存 — golden 在 S3 上,CI 仅根据校验和下载更改的文件。在 IT Sectr,我们使用 Git LFS,配置为 track *.png filter=lfs diff=lfs merge=lfs text=false。本地 golden 位于 src/test/goldens/ 中。

Golden 代码审查 — 普通的 git diff 不显示 PNG 更改。解决方案:(1)GitHub 在点击时打开 PNG 图像。(2)使用 Review Apps,其中 golden 差异在浏览器中可见。(3)生成带有之前/之后/差异列的 HTML 报告。Paparazzi 创建具有三列的 HTML 报告:actual、expected、diff。报告附加到 CI 产物中。审查者无需本地下载文件即可查看报告。

何时更新 golden — 仅在有意的 UI 更改之后。更改字体、颜色、间距、图标 — golden 应更新。添加新按钮、重新排列元素 — golden 应更新。改变外观的错误修复 — golden 应更新。不更改 UI 的重构 — golden 不应更新。如果 golden 在未更改 UI 代码的情况下发生变化 — 则这是由环境引起的不稳定测试,请在 CI 代理或依赖版本中查找原因。

常见问题

Golden Test 与 Screenshot Test 有什么区别?

Golden Test — 在单元测试环境中的组件级别快照测试(快速,无需模拟器)。Screenshot Test — 在设备或模拟器上捕获整个屏幕(慢速,但真实)。Golden 使用离屏缓冲区,screenshot 使用真实显示器。Golden 适用于每次提交的 CI,screenshot 适用于发布前的每晚构建。

如何处理不稳定的 golden 测试?

主要原因:(1)CI 上不同的 GPU — 使用相同的 CI 代理。(2)不同的字体版本 — 固定操作系统版本。(3)不同的抗锯齿 — 配置阈值(Roborazzi,iOSSnapshotTestCase)。(4)动画 — 在测试中禁用动画。(5)系统元素(状态栏)— 使用无边框设备配置。Paparazzi 由于 Layoutlib 不易受不稳定影响。

Golden Test 可以与 Jetpack Compose 一起使用吗?

可以。Paparazzi 通过 paparazzi.snapshot { } 内置了对 Compose 的支持。Roborazzi 也支持 Compose。在 iOS 上,SwiftSnapshotTesting 通过 UIHostingController 与 SwiftUI 配合使用。Compose 组件通过 Layoutlib 渲染,SwiftUI — 通过 UIKit 渲染。限制:Compose 和 SwiftUI 动画不受支持 — golden 测试仅捕获初始状态。

如何自动接受 golden 更改?

切勿在 CI 上自动化 golden 接受。仅在本地:开发人员从目录中删除旧的 golden 文件并使用记录标志运行测试(Paparazzi:record=true,SwiftSnapshotTesting:record=true)。Golden 文件被重新创建。开发人员检查每个 golden 的正确性,将更改与代码一起提交。在 CI 上自动接受将导致 UI 错误被忽略。

Golden Test 会减慢构建速度吗?

Golden 测试比工具测试(UI Automator,XCUITest)更快。一个 golden 测试 在 50-200 毫秒内执行(Paparazzi:在普通 MacBook Pro 上为 100-150 毫秒)。500 个 golden 测试 = 25-100 秒。与通过模拟器的截图测试相比:每个测试 5-30 秒。Golden 测试不会减慢构建速度:100 个测试 = 约 15 秒,这对于合并前验证来说是可以接受的。

总结

  • Golden Test — 通过与基准 PNG 图像进行比较来可视化测试 UI 组件
  • 过程 — 在离屏缓冲区中渲染组件,逐像素比较,不匹配时生成差异
  • Android — Paparazzi(Compose/View,Layoutlib)和 Roborazzi(Compose/View,阈值,Robolectric)
  • iOS — SwiftSnapshotTesting(pointfree)和 Uber 的 iOSSnapshotTestCase 用于 UIKit 和 SwiftUI
  • CI 管道 — 每次 PR 上的 golden 测试,差异产物,仅本地 golden 更新
  • Git LFS — 存储 PNG 文件所必需(500 个测试为 100-400 MB)
  • Flakiness — 与 GPU、字体和抗锯齿相关;通过阈值和相同的 CI 代理解决

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读