GitHub Actions 中的视觉测试是在 GitHub Actions 工作流中集成自动化的截图捕获和比较步骤,通过将应用程序的当前截图与已验证的视觉基准进行对比,在允许合并或部署之前检测任何显示回归。
GitHub Actions 已成为全球使用最广泛的 CI/CD 平台。根据 JetBrains 2024 年开发者生态系统报告,超过 50% 的团队将其用作主要的持续集成系统。这很合理:它原生集成在 GitHub 中,对开源项目免费,且足够灵活以覆盖大多数需求。
然而,绝大多数这些工作流仅仅运行单元测试和集成测试。代码通过了,流水线是绿色的,pull request 被合并了。没有人检查首页是否已经变成了一堆错位的组件。
我们的立场很明确:GitHub Actions 中的视觉测试应该像单元测试一样标准化。 不是额外奖励。不是"有了更好"。而是工作流的基本步骤,与 linting 或功能测试处于同一级别。
本指南将逐步为你讲解如何实现这一目标。
流水线是绿色的,但没有人检查过首页是否已经坏掉。 借助 Delta-QA,把视觉比较加入你的 GitHub Actions 工作流(面向 CI 的 SaaS 方案)——先在本地免费测试即可上手。免费使用 Delta-QA →
目录
- 为什么 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 失败并阻止合并。
这一点不容商量。 视觉测试的结果如果埋在工作流日志中,永远不会被查看。结果必须是可见的、即时的,并集成到开发者的正常工作流程中。
无需付费云服务也能让你的 GitHub PR 更可靠。 借助 Delta-QA,在本地、无需注册即可运行视觉比较,无需依赖第三方服务器就能补全你的 GitHub Actions 流水线。免费使用 Delta-QA →
No-Code 方法与脚本方法
两种理念在 GitHub Actions 的视觉测试中竞争。
脚本方法
你编写 Playwright 或 Cypress 脚本,导航到你的页面,截图并比较它们。你维护这些脚本、管理选择器,并手动更新基准。
这种方法提供完全的控制。但代价不小。UI 变化时脚本会中断。选择器会过时。维护成为全职工作。最重要的是,只有开发者能创建和维护测试。
No-Code 方法
你使用一个集成管理截图、比较和报告的工具。你在可视界面中定义要测试的页面。GitHub Actions 集成通过即用型 action 或 webhook 完成。
这种方法更易于使用。QA 工程师、设计师和产品经理可以在不编写代码的情况下配置和验证视觉测试。维护减少了,因为 UI 演变时没有脚本需要更新。
我们的立场是 no-code 方法对大多数团队更可取。 不是因为脚本方法不好,而是因为视觉测试不应该仅限于开发者。视觉回归是设计和产品问题,不仅仅是代码问题。
Delta-QA 正是采用了这种方法。你连接你的 GitHub 仓库,定义要监控的页面,视觉测试在每个 pull request 上自动运行。无需编写脚本。无需复杂的 YAML 配置。只有结果,直接在你的 PR 上。
四种可用方案的对比
No-Code 与脚本方法是一种理念上的划分,但在 GitHub Actions 中实现视觉测试时,你实际面对的是四种具体方案。每一种在成本、依赖性和合规性上有不同的取舍。
方案 1:Playwright + toHaveScreenshot()
Playwright 是目前最适合 CI 中视觉测试的开源工具。其 toHaveScreenshot() 方法处理截取、比较和基准图片存储。你编写测试导航到页面、等待内容稳定、截图并与仓库中的基准图片比较;工作流安装 Playwright 及其浏览器,执行测试并报告结果。
优势。 一切都是开源且免费的,基准图片留在你的仓库中,无需外部服务,且原生支持自动重试的视觉稳定等待。
实际局限。 首次生成基准图片必须在 CI 环境中完成,而不是在本地——这是很多人调试误报数小时后才发现的黄金法则。在 Mac 上生成的基准图片不会与 Ubuntu runner 的渲染匹配。此外,每次有意的视觉变更都需要用 --update-snapshots 更新基准图片;对单个测试很简单,对 200 个页面则本身就是一个流程。
方案 2:云服务(Percy、Chromatic、Applitools)
云端视觉测试服务提供官方的 GitHub Actions action。你将官方 action 添加到工作流、配置 API 令牌,每次 push 触发视觉捕获;服务负责比较、多浏览器渲染和审查仪表板,结果作为 check 显示在 pull request 上。
优势。 你将非确定性渲染问题外包——云服务在受控且稳定的环境中渲染页面。审查仪表板专业,跨浏览器测试无需配置即可运行。
局限。 成本是核心取舍:所有这些服务按快照量收费,随着应用增长价格快速上升。依赖外部服务也意味着他们的故障会阻塞你的 merge request(如果你将 check 配置为必需)。而且你的截图经过第三方基础设施传输,这可能引发合规性问题。
方案 3:BackstopJS(Docker / JSON 配置)
BackstopJS 是一个可通过 JSON 场景配置的开源视觉回归工具,通过 Docker 容器或直接安装在 GitHub Actions 中运行。你定义场景(URL、视口、要捕获的选择器),它截取屏幕截图并与基准图片比较,HTML 报告作为工作流的 artifact 生成。
优势。 开源、免费,HTML 报告比原始图片 diff 更易读。
局限。 对于复杂应用,通过 JSON 场景的配置变得冗长。该项目的维护经历过不均匀的阶段。与 Playwright 一样,在不同环境中生成基准图片的问题依然存在。
方案 4:Delta-QA
Delta-QA 不要求你编写测试脚本、在 Git 中管理基准图片或调试与环境相关的误报,而是自主处理截取和比较。你的工作流触发 Delta-QA,它在稳定、受控的渲染环境中捕获页面;基准图片由工具管理,而不是你的 Git 仓库,因此与环境差异相关的误报消失了。当检测到差异时,它出现在专用审查界面中——不是在 PNG 文件夹或 500 行的 CI 日志里——你的 QA 团队和设计师无需访问 GitHub 即可审查。视觉测试也不与你的测试栈耦合,应用演进时无需更新脚本。
优化执行时间
视觉测试天然比单元测试慢——需要打开浏览器、加载页面、等待稳定、捕获截图。在 GitHub Actions 中,每一分钟都很重要(如果你为 runner 付费,这是字面意义上的)。
并行化。 GitHub Actions 支持策略矩阵。将视觉测试分布到多个并行 job 中以缩短总时间。
针对变更。 如果一个 commit 只涉及特定组件,就没必要对整个应用进行视觉测试。一些工具允许根据修改的文件来定向测试。
缓存浏览器。 通过 Playwright 安装 Chromium 需要时间。使用 GitHub Actions 缓存避免每次运行都下载。
使用更强大的 runner。 标准 GitHub Actions runner 适合单元测试,但对于渲染复杂页面来说能力有限。大型 runner 或 self-hosted runner 显著减少捕获时间。
配置中应避免的错误
多年观察 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 就会自动进行视觉检查。
是时候以对待应用逻辑同样认真的态度来对待应用的外观了。
准备好让 PR 因为真正的视觉回归而失败,而不是因为噪声了吗? 用 Delta-QA 免费、无需注册地运行你的第一次视觉比较,然后再把它自动化到 GitHub Actions 中。免费使用 Delta-QA →