3. Roborazzi 库详解:介绍、集成与第一个快照测试
讲快照测试,就绕不开 Roborazzi。说实话,我第一次接触它是在一个跨团队合作的项目里——团队七八个人同时改 UI,回归测试每次都跑得肝疼。后来我试着把 Roborazzi 塞进流水线,嗯,效果出乎意料得好。
3.1 什么是 Roborazzi?
Roborazzi 是 Dropbox 开源的一个 Android 快照测试工具。它专门为 Compose 和 View 系统设计,底层用 JVM 上的 Robolectric 来渲染,不需要真机或者模拟器。说白了,你在本地跑一条命令,就能拿到 Compose 组件的截图,然后跟基准图做像素级对比。
常见截图测试库的对比:
| 工具 | 运行环境 | Compose 支持 | 图像对比 | 上手难度 |
|---|---|---|---|---|
| Roborazzi | JVM(Robolectric) | 原生支持 | 像素级 | 低 |
| Paparazzi | JVM | 需额外配置 | 像素级 | 中 |
| Screenshot Tests for Android | 真机/模拟器 | 有限支持 | 模糊对比 | 高 |
从表格就能看出来,Roborazzi 在 Compose 领域的兼容性和上手成本上都有优势。我自己的项目一开始用的 Paparazzi,后来发现每次改个 padding 都要等模拟器启动,果断切到了 Roborazzi。
3.2 为什么选 Roborazzi?
- 跑得快: 基于 Robolectric,无需启动模拟器,单条测试几百毫秒搞定。
- 对比准: 支持像素级 diff,连 1px 的偏移都能报警。
- 集成简单: 加个依赖、写个 test 类,十分钟就能跑出第一张快照。
- 报告直观: 自动生成 HTML 报告,左边基准、右边新图、中间 diff,一眼看出哪变了。
这里我要提一句,Roborazzi 不仅支持 Compose,也支持 View 系统的截图。但既然咱们在做 Compose 课程,我后面所有示例都会用 Compose 组件来演示。
3.3 集成到项目中
集成步骤非常简单,咱们一步步来。
第一步:添加插件和依赖(项目级 build.gradle.kts)
// 在根 build.gradle.kts 的 plugins 中
id("io.github.takahirom.roborazzi") version "1.12.0" apply false
// 在 module 级 build.gradle.kts 中
plugins {
id("io.github.takahirom.roborazzi")
}
dependencies {
testImplementation("io.github.takahirom.roborazzi:roborazzi:1.12.0")
testImplementation("io.github.takahirom.roborazzi:roborazzi-compose:1.12.0")
testImplementation("io.github.takahirom.roborazzi:roborazzi-junit-rule:1.12.0")
}
第二步:配置输出目录(同样在 module 级 build.gradle.kts)
roborazzi {
// 基准截图存放路径
baselineDir = "src/test/snapshots/baseline"
// 新截图和 diff 图输出路径
outputDir = "build/reports/roborazzi"
}
第三步:编写测试基类(可选)
我个人习惯写一个抽象基类,把 Compose 测试环境统一管理起来:
import androidx.compose.runtime.Composable
import androidx.compose.ui.test.junit4.createComposeRule
import org.junit.Rule
import org.junit.rules.TestRule
import io.github.takahirom.roborazzi.RoborazziRule
abstract class ComposeSnapshotTest {
@get:Rule
val composeRule = createComposeRule()
@get:Rule
val roborazziRule: TestRule = RoborazziRule(
// 截图规则可以自定义输出文件名
options = RoborazziRule.Options(
outputFile = "build/reports/roborazzi/{classSimpleName}/{methodName}.png"
)
)
protected fun setContent(content: @Composable () -> Unit) {
composeRule.setContent(content)
}
}
3.4 编写第一个快照测试
假设咱们有个简单的 Compose 按钮组件:
@Composable
fun MyButton(text: String, onClick: () -> Unit) {
Button(onClick = onClick) {
Text(text)
}
}
写快照测试只需要三步:
- 继承咱们的基类(或用 createComposeRule + RoborazziRule)。
- 在 setContent 里放入待测试组件。
- 调用
RoborazziRule.captureView()或直接用注解自动截图。
完整代码:
import io.github.takahirom.roborazzi.RoborazziRule
import org.junit.Test
class MyButtonSnapshotTest : ComposeSnapshotTest() {
@Test
@RoborazziRule(// 可以加选项,比如 captureType = RoborazziRule.CaptureType.Screenshot
)
fun buttonTest() {
setContent {
MyButton(text = "Click Me") { }
}
}
}
跑测试:
./gradlew testDebugUnitTest
第一次运行会自动生成基准截图(baseline),存放在你配置的 baselineDir 下。第二次运行会比对,如果图片不一致,测试会失败并在 outputDir 生成新的截图和 diff 图。
AnimationClockRule 固定动画帧,或者直接禁用动画。Roborazzi 提供了 RoborazziRule.Options(recordPrerecorded = true) 来处理这类问题,有兴趣的可以查一下文档。
下面这张图能帮你理清 Roborazzi 的整个工作流程:
图中的核心流程很清楚:测试启动 → Robolectric 渲染 → 截图 → 判断是否需要生成基线 → 与基线对比 → 输出报告。理解了这个流程,后面调试就有的放矢了。