GitHub Actions 中的视觉测试是在 GitHub Actions 工作流中集成自动化的截图捕获和比较步骤,通过将应用程序的当前截图与已验证的视觉基准进行对比,在允许合并或部署之前检测任何显示回归。
GitHub Actions 已成为全球使用最广泛的 CI/CD 平台。根据 JetBrains 2024 年开发者生态系统报告,超过 50% 的团队将其用作主要的持续集成系统。这很合理:它原生集成在 GitHub 中,对开源项目免费,且足够灵活以覆盖大多数需求。
然而,绝大多数这些工作流仅仅运行单元测试和集成测试。代码通过了,流水线是绿色的,pull request 被合并了。没有人检查首页是否已经变成了一堆错位的组件。
我们的立场很明确:GitHub Actions 中的视觉测试应该像单元测试一样标准化。 不是额外奖励。不是"有了更好"。而是工作流的基本步骤,与 linting 或功能测试处于同一级别。
本指南将逐步为你讲解如何实现这一目标。
目录
- 为什么 GitHub Actions 是视觉测试的理想平台
- 你(可能)正在忽视的问题
- 视觉测试工作流的五个步骤
- Checkout:获取代码和基准
- Build:在 CI 环境中构建应用
- Capture:自动捕获截图
- Compare:检测视觉差异
- Report:在 Pull Request 上传达结果
- No-Code 方法与脚本方法
- 配置中应避免的错误
- 常见问题
- 结论
为什么 GitHub Actions 是视觉测试的理想平台
GitHub Actions 拥有使其成为视觉测试天然平台的特性。最受欢迎的视觉测试工具优先提供 GitHub Actions 集成并非巧合。
与 pull request 的原生集成。 这是决定性优势。GitHub Actions 在每个 pull request 上运行,其结果直接显示在 PR 界面中。视觉测试失败会阻止合并——就像单元测试失败一样。无需在多个工具或标签页之间切换。
环境是短暂且可复现的。 每次工作流执行都在全新的 runner 上启动。这消除了视觉测试的一个经典问题:由机器状态引起的渲染差异。在 GitHub Actions 中,每次运行的环境都是相同的。截图可以从一次运行比较到下一次。
marketplace 充满了即用型 actions。 你不需要从零开始构建一切。社区维护的 actions 让你只需在 YAML 文件中写几行就能安装必要的依赖(headless 浏览器、截图工具)。
artifacts 允许你存储视觉结果。 GitHub Actions 允许你将文件(截图、差异报告)保存为附加到每次运行的 artifacts。你的团队可以直接从 GitHub 界面查看截图和差异。
你(可能)正在忽视的问题
如果你在一个规模较大的前端项目上工作——电商网站、SaaS 应用、设计系统——你很可能已经经历过这种情况。
一个开发者修改了一个共享组件。比如主按钮。调整了 padding、颜色或 border-radius。单元测试通过了。Cypress 或 Playwright 测试通过了。构建是绿色的。PR 被批准并合并了。
第二天,有人注意到结账页面的按钮变得很小,因为被修改的组件在一个具有特定样式的上下文中使用,这些样式与旧的 padding 产生了交互。或者按钮在移动端与相邻元素重叠。
这类回归对传统测试是不可见的。功能测试验证按钮可以点击且关联的操作能正常工作。它不会验证按钮是否可见、可读,以及在其视觉上下文中是否正确定位。
视觉测试是唯一能可靠且自动化地检测此类问题的方法。 它从字面上比较修改前后的页面图像。如果有任何视觉变化,你就会知道。
视觉测试工作流的五个步骤
GitHub Actions 中的视觉测试工作流遵循一个五步逻辑模式。每一步都有其角色和细微之处。
步骤 1:Checkout——获取代码和基准
第一步是标准的:获取项目的源代码。但在视觉测试的上下文中,它有一个重要的特殊之处。你还必须获取视觉基准——用于与新截图进行比较的基线截图。
存储这些基准有两种策略。要么将它们提交到仓库中(简单但会增加仓库体积),要么将它们存储在外部服务中并在工作流期间下载(更干净但配置更复杂)。
你选择的策略取决于你的项目。对于只需检查少量页面的项目,将基准提交到仓库是完全可以接受的。对于有数百张截图的项目,外部存储更可取。
关键是 checkout 必须带回比较所需的一切:当前代码,以及对应目标分支(通常是 main)的视觉基准。
步骤 2:Build——构建应用
在截图之前,应用需要运行起来。这一步类似于你已经为 end-to-end 测试所做的:安装依赖、构建项目并启动本地服务器。
一些视觉测试特有的注意事项。
稳定环境。 字体是误报的常见来源。在 GitHub Actions runner 上(默认为 Ubuntu),安装的字体与你的开发机器不同。确保安装应用使用的字体,或使用通过 CDN 加载的 web 字体。
禁用动画。 CSS 动画会根据截图的确切时刻生成不同的截图。注入一个禁用所有过渡和动画的样式。这是标准且必要的做法。
等待服务器就绪。 在本地服务器完成启动之前不要开始截图。工作流中的一个简单健康检查就足够了。
步骤 3:Capture——捕获截图
这是流程的核心。headless 浏览器(Chromium、Firefox 或 WebKit)导航到每个需要检查的页面或组件并截图。
这一步的质量取决于两个因素。第一是覆盖范围:你截取哪些页面和组件?第二是稳定性:对于相同的代码,每次运行的截图是否相同?
关于覆盖范围,从最关键的页面开始。首页、产品页、结账页、主仪表盘。不需要一开始就截取 500 个页面。精心选择的二十个页面已经提供了相当大的价值。
关于稳定性,几种技术是必不可少的。遮蔽或冻结动态元素:时钟、计数器、广告、随机头像。使用可预测的测试数据。等待页面完全加载(包括图片和 web 字体)后再截图。
使用 Delta-QA 这样的 no-code 工具,这一步大大简化了。你在可视界面中定义要截取的页面,无需编写 Playwright 或 Puppeteer 脚本。工具为你处理稳定化和截图。
步骤 4:Compare——检测视觉差异
新鲜的截图与基准进行比较。比较算法分析每个像素——或使用更智能的感知方法,忽略不重要的亚像素差异。
结果是按严重程度排列的差异列表。按钮颜色变化是重要的。文本上略有不同的抗锯齿是噪声。
容忍阈值至关重要。 太敏感,你会淹没在误报中。太宽松,你会错过真正的回归。大多数工具允许你按页面或区域配置此阈值。
感知比较优于原始的逐像素比较。 它容忍轻微的渲染变化(抗锯齿、亚像素渲染),同时检测视觉上显著的变化。如果你的工具不提供此选项,预计在 GitHub Actions 上会有许多误报,因为不同版本的 runner 之间渲染可能略有不同。
步骤 5:Report——传达结果
最后一步与前面的步骤同样重要。如果没有人看到结果,测试就没有价值。
在 GitHub Actions 中,理想的报告直接在 pull request 上进行。自动评论显示检测到的差异,并排展示截图:基准、当前截图和高亮的差异。
GitHub check 状态(PR 上著名的绿色/红色)必须反映视觉测试的结果。如果检测到未批准的差异,check 失败并阻止合并。
这一点不容商量。 视觉测试的结果如果埋在工作流日志中,永远不会被查看。结果必须是可见的、即时的,并集成到开发者的正常工作流程中。
No-Code 方法与脚本方法
两种理念在 GitHub Actions 的视觉测试中竞争。
脚本方法
你编写 Playwright 或 Cypress 脚本,导航到你的页面,截图并比较它们。你维护这些脚本、管理选择器,并手动更新基准。
这种方法提供完全的控制。但代价不小。UI 变化时脚本会中断。选择器会过时。维护成为全职工作。最重要的是,只有开发者能创建和维护测试。
No-Code 方法
你使用一个集成管理截图、比较和报告的工具。你在可视界面中定义要测试的页面。GitHub Actions 集成通过即用型 action 或 webhook 完成。
这种方法更易于使用。QA 工程师、设计师和产品经理可以在不编写代码的情况下配置和验证视觉测试。维护减少了,因为 UI 演变时没有脚本需要更新。
我们的立场是 no-code 方法对大多数团队更可取。 不是因为脚本方法不好,而是因为视觉测试不应该仅限于开发者。视觉回归是设计和产品问题,不仅仅是代码问题。
Delta-QA 正是采用了这种方法。你连接你的 GitHub 仓库,定义要监控的页面,视觉测试在每个 pull request 上自动运行。无需编写脚本。无需复杂的 YAML 配置。只有结果,直接在你的 PR 上。
配置中应避免的错误
多年观察 GitHub Actions 中的视觉测试工作流揭示了反复出现的错误。以下是最常见的。
不固定浏览器版本
GitHub Actions runner 定期更新 Chromium。浏览器版本变化可能改变某些元素的渲染(抗锯齿、字体渲染、间距)。结果:所有视觉测试一次性全部失败,而没有任何代码更改。
解决方案是固定用于截图的浏览器版本,或使用一个为你管理此方面的工具。
忽略 web 字体加载时间
Google Fonts 和其他 web 字体有时需要几百毫秒才能加载。如果你太早截图,会得到使用后备字体的渲染。测试因错误的原因而失败。
在每次截图前明确等待字体完全加载。这是 Delta-QA 等 no-code 工具原生处理的一点。
过早测试太多页面
初始的热情推动团队截取网站的每一页。结果:数十个误报、流水线时间爆炸、团队在两周后禁用视觉测试。
从五到十个关键页面开始。在工作流稳定且团队养成处理结果的习惯后,再逐步添加。
不将结果集成为阻止性 check
如果视觉测试只是工作流中的信息性步骤,它将被忽视。在 GitHub 的分支保护规则中将其配置为必需的 check。没有视觉验证就不能合并。
不为预期变更规划批准机制
任何有意的 UI 修改都会触发视觉测试。必须有一个清晰的流程来批准这些变更并更新基准。没有这个流程,视觉测试会成为障碍而不是工具。
常见问题
GitHub Actions 中的视觉测试会显著减慢我的流水线吗?
增加的时间取决于截取的页面数量。对于具有十到二十个页面的典型项目,预计增加两到五分钟。与在生产中发现视觉回归的调试时间相比,这是一项微小的投资。Delta-QA 等 no-code 工具通过并行化截图来优化这一时间。
我可以在来自 fork 的 pull request 上使用视觉测试吗?
可以,但需要注意。由 fork 触发的工作流默认无法访问仓库的 secrets。如果你的视觉测试工具需要 API 密钥,你需要配置工作流使用 pull_request_target 触发器,它在目标仓库的上下文中运行。请参阅 GitHub 文档了解安全影响。
我应该分别截取移动端和桌面端版本吗?
当然。在桌面端完美显示的页面在移动端可能无法阅读。配置不同的 viewport(例如桌面端 1440 像素宽,移动端 375 像素)并在两种分辨率下截取每个页面。这会使截图数量翻倍,但响应式回归是最常见且影响最大的回归之一。
如何处理每次加载都会变化的动态内容?
有几种策略。你可以在测试配置中遮蔽动态区域(广告、时间戳、计数器)。也可以通过 mock 或 fixture 使用固定的测试数据。一些工具提供区域比较功能,自动忽略标记为动态的区域。重要的是在截图前稳定内容以避免误报。
视觉测试能替代 Cypress 或 Playwright 的 end-to-end 测试吗?
不能,这也不是它的目的。End-to-end 测试验证功能行为:用户流程从头到尾是否正常工作?视觉测试验证外观:界面看起来是否符合预期?这是两个互补的层次。End-to-end 测试验证按钮是否工作;视觉测试验证按钮是否可见且正确渲染。
视觉测试能在自托管的 GitHub Actions runner 上工作吗?
能,有时甚至更可取。自托管 runner 提供比 GitHub 托管 runner 更稳定的环境,因为你可以控制浏览器版本、安装的字体和系统配置。这减少了与环境变化相关的误报。但是,你必须确保必要的图形依赖(X11 库、字体)安装在你的 runner 上。
结论
GitHub Actions 中的视觉测试不是保留给拥有大量 QA 预算的大型团队的异国功能。它是一项基本实践,应该成为每个工作流的一部分,与 linting、单元测试和集成测试并列。
GitHub Actions 提供了正确集成所需的一切:可复现的 runner、与 pull request 的原生集成、用于存储结果的 artifacts,以及在出现回归时阻止合并的 check 系统。
障碍不再是技术性的。而是文化性的。太多团队仍然将视觉测试视为奢侈品,却毫无疑问地接受将视觉回归交付到生产环境。
如果你已经在使用 GitHub Actions——从统计上看,有二分之一的概率是这样——你没有理由不将视觉测试添加到你的工作流中。Delta-QA 等 no-code 工具使集成变得简单。几分钟的配置,每个 pull request 就会自动进行视觉检查。
是时候以对待应用逻辑同样认真的态度来对待应用的外观了。