Paparazzi 库详解:介绍、集成与特点对比

先聊聊 Paparazzi 是什么

说实话,我第一次接触 Paparazzi 的时候,也被它名字唬住了——还以为是什么狗仔队工具。实际上它就是 Google 开源的一个截图测试库,专门给 Android 用,尤其适合 Compose。它的核心思路很简单:把你的 UI 在 JVM 上渲染成图片,然后跟之前保存的基准快照做对比。

我个人的理解是——它跳过了模拟器和真机,直接在开发机里完成截图。怎么做到的?靠的是 Layoutlib,也就是 Android Studio 预览那个引擎。所以跑一次测试,几秒就能拿到对比结果。嗯,这一点对 CI 来说特别友好。

小提示: Paparazzi 不依赖设备或模拟器,所有截图都在 JVM 进程内完成。你想想看,团队里每个人都不用装模拟器,拉下代码直接跑测试,多省事。

集成步骤,我踩过的坑

集成 Paparazzi 其实不复杂,但有些细节容易翻车。我直接贴配置步骤,顺便说说我当初遇到的。

第一步:添加插件和依赖

在项目的 build.gradle.kts(根级别)里加插件:

plugins {
    id("app.cash.paparazzi") version "1.3.1" apply false
}

然后在 app 模块里:

plugins {
    id("app.cash.paparazzi")
}

dependencies {
    // 如果是 Compose,需要额外加:
    testImplementation("app.cash.paparazzi:paparazzi:1.3.1")
    testImplementation("androidx.compose.ui:ui-test-junit4:1.5.0")
}

我曾经踩过的一个坑:忘记把 paparazzi 插件加到 app 模块,结果测试跑起来报错说找不到 snapshot 目录。后来读文档才发现插件和依赖都得配齐。

第二步:写测试类

创建一个测试类,用 @RunWith(PaparazziTestRunner::class) 注解,然后写个测试方法,把 Composable 传给 paparazzi.snapshot():

@RunWith(PaparazziTestRunner::class)
class MyComposableTest {

    @get:Rule
    val paparazzi = Paparazzi()

    @Test
    fun testButtonSnapshot() {
        paparazzi.snapshot {
            MyButton(text = "点击我")
        }
    }
}

跑测试时,Paparazzi 会自动生成一张 PNG 放在 src/test/snapshots/images/ 下。第一次跑是创建基准快照,后续跑就是做对比。

注意: 基准快照要纳入版本控制。团队其他成员拉下来后,直接就能对比,不需要重新生成。我见过有人把 snapshots 目录加进 .gitignore,结果 CI 上每次都是新截图,对比永远失败。千万别这么干。

跟其他截图方案比,它好在哪?

现在市面上截图测试方案不少:Robolectric + 手动截图、Compose 自带的 captureToImage()、还有 Firebase Test Lab 的截图。我简单列个对比表格,这样更直观。

特性 Paparazzi Robolectric + Screenshot Compose captureToImage 真机截图 (Firebase)
运行环境 JVM (无需模拟器) JVM (需 Robolectric 阴影) Android 设备/模拟器 真机集群
渲染引擎 Layoutlib (同预览) Robolectric 自定义 Compose 运行时 真机 GPU
速度 极快(秒级) 快(但需 Shader 编译) 较慢(需部署到设备) 慢(排队 + 启动)
像素级对比 内置 需额外库 需手写 需第三方服务
Compose 支持 原生 需额外配置 原生 原生
CI 友好度 ★★★★★ ★★★★ ★★ ★★★

我个人的经验是:如果你的项目主要用 Compose,而且团队希望 CI 快速反馈,Paparazzi 几乎是首选。我见过有人用 Robolectric 做截图,结果因为阴影问题和真实渲染差距大,经常出现「英雄时刻」——本地跑通过,CI 上对比失败,折腾半天发现是 Robolectric 的字体渲染跟真机不一样。

核心优势一句话: Paparazzi 用 Android Studio 的预览引擎渲染,跟你在 IDE 里看到的几乎一致,所以基准快照的可信度非常高。

工作流程一览

下面这张图是我自己画的流程图,把 Paparazzi 从编写测试到出结果的过程串了一遍——

Paparazzi 快照测试工作流程 1. 编写测试 (SnapshotTest) 2. 执行 gradlew test 3. Layoutlib 渲染 Composable → Bitmap 4. 保存 PNG (snapshots/) 5. 与基准快照对比 6. 通过 / 失败 (差异报告) 首次运行:生成基准快照 后续运行:像素级对比

整个流程走下来,你会发现在开发阶段「编写 → 运行 → 对比」的反馈非常快。我习惯每次写新组件时先跑一次测试生成基准图,后面修改后对比出差异,一眼就能看出哪里变了。

需要留意的几个细节

  • 主题和资源问题:Paparazzi 默认使用应用的 Theme.Material3.Light,如果你用了自定义主题或者深色模式,记得在测试里手动设置。我有一次就因为这个,截图全是亮色主题,跟基准不一致。
  • 动画和异步:Paparazzi 不会执行动画帧,所以 animateFloatAsState 之类的效果默认是初始状态。如果需要特定状态,可以配合 ComposeTestRule 手动设置。
  • 字体差异:JVM 上用的系统字体跟你模拟器/真机可能不同,尤其是在 CI 服务器上。我建议在测试里显式指定 Roboto 或 Noto 等常见字体,避免跨平台差异。
重点提醒: 别忘了在 CI 上配置 recordMode。我见过团队不小心把 recordMode = true 提交到 Git,结果每次 CI 跑都是重新记录基准,永远不会报失败——等于测试白写了。正确做法:本地开发用 record 生成基准,CI 用默认的对比模式。

总结一句话

Paparazzi 的核心价值在于「快」和「准」。快是指不依赖模拟器,几秒出结果;准是指用 Layoutlib 渲染,跟 IDE 预览一致。如果你正在为 Compose 项目寻找截图测试方案,我个人强烈建议从 Paparazzi 开始。它在 CI 上的表现,能让你省下不少排查时间。