视觉 UI 测试(也称为回归测试)是一种测试技术,用于验证您的更改是否对 UI 产生了意外的影响。通常,这类测试会对整个应用程序或特定元素进行图像快照,并将其与之前批准的基准图像进行比较。如果图像相同(在设定的像素容差范围内),则可以确定 web 应用程序的外观对用户来说没有变化。如果存在差异,则可能是 DOM 布局、字体、颜色或其他视觉属性发生了变化,需要进一步调查。
这篇文章将探讨如何使用 Playwright 和 GitHub Actions 自动化测试“现代”web 应用程序的视觉回归测试。目标是构建一个测试设置,在每个 Pull Request 上检查 UI 回归,并在需要时选择性地更新基准图像。
建议具备基本的 JavaScript(或 TypeScript)和 GitHub Actions 知识。
我们假设您会将此设置集成到现有的 web 应用程序中。如果您想从头开始尝试,建议使用 Vite 搭建一个新的 web 应用程序。
您可以在这个 GitHub 仓库 中找到完整的示例应用程序。
以下是我们正在测试的小型 web 应用程序的外观:
Playwright 设置
对于我们的测试设置,将使用 Playwright,这是一个端到端(E2E)测试框架。我喜欢 Playwright,因为它提供了优秀的开发体验和默认配置。不过,您也可以用类似工具(如 Cypress 或 Selenium)实现相同的结果。
安装 Playwright
进入项目目录并运行以下命令:
1 | npm init playwright |
运行后会提示您进行一些选择(例如 JavaScript 或 TypeScript 等)。当被询问是否添加 GitHub Actions 工作流时,选择 true
以便于在 CI 上运行测试:
1 | ✔ Add a GitHub Actions workflow? (y/N) · true |
安装完成后,Playwright 会生成一个示例测试文件(./tests/example.spec.ts
)、一个演示文件(./tests-examples/demo-todo-app.spec.ts
,可以忽略),以及配置文件(./playwright.config.ts
)。
1 | ✔ Success! Created a Playwright Test project at ~/your-project-dir |
在此目录中,您可以运行以下命令:
npx playwright test
运行端到端测试。npx playwright test --project=chromium
仅在桌面 Chrome 上运行测试。npx playwright test example
运行特定文件中的测试。npx playwright test --debug
在调试模式下运行测试。npx playwright codegen
自动生成测试代码。
建议从以下命令开始:
1 | npx playwright test |
并查看以下文件:
./tests/example.spec.ts
- 示例端到端测试文件./tests-examples/demo-todo-app.spec.ts
- 示例 ToDo App 测试文件./playwright.config.ts
- Playwright 配置文件
访问 Playwright 文档 获取更多信息。✨
修改 Playwright 配置文件
Playwright 在生成的配置文件(./playwright.config.ts
)中提供了一些不错的默认设置,但可以做一些改进。
简化测试浏览器配置
更新projects
列表,仅在 Chromium 上运行测试:1
2
3
4
5
6
7
8projects: [
{
name: "chromium",
use: {
...devices["Desktop Chrome"],
},
},
]需要时,可以稍后再添加更多浏览器或设备支持。
自动启动本地服务器
配置webServer
部分,以便 Playwright 在运行测试时自动启动应用程序:1
2
3
4
5webServer: {
command: 'npm run dev --port 8080',
port: 8080,
reuseExistingServer: true,
},
生成初始快照
Playwright 生成的示例测试文件(./tests/example.spec.ts
)不是视觉回归测试,因此需要替换为适合的测试代码:
1 | import { test, expect } from "@playwright/test"; |
Playwright 支持通过 await expect(page).toHaveScreenshot()
生成并比较快照。首次运行时,会生成基准快照;后续运行将与基准进行比较。
运行测试命令:
1 | npx playwright test |
运行后,您可能会看到类似以下信息:
1 | Error: example.spec.ts-snapshots/example-test-1-chromium-darwin.png is missing in snapshots, writing actual. |
这是因为还没有基准图像,测试会不断生成快照,直到两次快照匹配,并将最后一次快照保存到文件系统中(例如:tests/example.spec.ts-snapshots/example-test-1-chromium-darwin.png
)。
再次运行测试:
1 | npx playwright test |
如果当前 UI 与之前生成的基准快照一致,测试将成功通过!
本地更新快照
让我们进入有趣的部分。
如果对 UI 进行了任何视觉更改并重新运行测试,测试会失败,Playwright 会显示“实际”与“预期”快照之间的清晰差异:
在这种情况下,如果我们希望页面的视觉变化是自愿的,就需要更新基准快照。可以使用 --update-snapshots
标志来完成:
1 | npx playwright test --update-snapshots |
示例输出:
1 | [chromium] › example.spec.ts:3:1 › example test |
现在,我们已经了解了如何运行视觉测试并在本地更新快照,接下来准备转向 CI 流程。
在 CI 上运行测试(使用 GitHub Actions)
GitHub Actions 是一个持续集成和持续交付(CI/CD)平台,可以自动化构建、测试和部署流水线。借助 GitHub Actions,可以为每次 Pull Request 创建和更新设置工作流,自动运行构建和测试。
一个良好的视觉回归测试设置起点是,在每次创建或更新 Pull Request 时运行测试。幸运的是,Playwright 已经在 .github/workflows/playwright.yml
中生成了一个方便的 GitHub Actions 工作流,专门用于此用例。
这个工作流可以开箱即用,但建议稍作调整:
- 只安装目标测试所需的浏览器(
--with-deps chromium
)。 - 仅在测试失败时保存测试结果工件(
if: failure()
),以避免存储不必要的文件。
以下是修改后的 playwright.yml
文件:
1 | name: Playwright Tests |
将此工作流提交到 GitHub 仓库后,每次提交新的 Pull Request 时,Playwright 测试将被触发。
注意:参考快照与 CI 环境的兼容性
测试在 CI 环境中运行时可能会失败,因为仓库中的基准快照可能是在与 CI 环境不同的机器上捕获的(除非这些快照是在运行 Ubuntu 的机器上生成的)。
要了解更多测试结果,可以检查 GitHub Action 的输出或查看测试工件。例如,如果在 macOS 上生成的基准快照会导致在 Linux 上的测试失败,错误可能会类似于:
1 | Error: tests/example.spec.ts-snapshots/example-test-1-chromium-linux.png is missing in snapshots |
接下来,我们将学习如何为 CI 环境更新参考快照。
在 CI 中通过 Pull Request 评论更新快照
使用 --update-snapshots
本地生成基准快照相对简单,而在 CI 中进行类似操作则更复杂,因为需要决定快照的存储位置、方式和时间。
有多种方法可以实现这个流程,为了简化,我们从简单的方式开始。
一种行之有效的模式是:通过 GitHub Action,当在 Pull Request 中发布包含 /update-snapshots
的特定评论时,触发工作流来更新基准快照。
基本思路如下:
当我们提交一个预计会影响 UI 的 Pull Request 时,可以添加一条 /update-snapshots
评论,这会触发 CI 自动生成更新后的快照,并将它们提交到 Pull Request 的分支中。
工作流:.github/workflows/update-snapshots.yml
1 | name: Update Snapshots |
运行流程概览
- 每当 Pull Request 中添加评论时,GitHub Actions 会触发此工作流。
- 若评论严格匹配
/update-snapshots
,则获取当前 Pull Request 的分支名和最新的提交 SHA。 - 检出对应分支并设置环境。
- 使用 Playwright 根据分支当前 UI 更新基准快照。
- 将更新的快照提交到 Pull Request 分支中。
发布此工作流后,创建新的 Pull Request 并添加 /update-snapshots
评论时,CI 将生成更新的快照。
运行 Playwright 测试以验证部署预览(Netlify、Vercel 等)
如果你的 Web 应用托管在 Netlify 或 Vercel 等平台上,你可能会使用它们的“部署预览”功能。这项功能允许你在不影响生产环境的情况下预览拉取请求的更改。部署预览默认对 GitHub 的拉取请求启用,它通过将更改部署到一个不同于生产站点的唯一 URL 来实现。
如果你的 Web 应用支持部署预览,我们可以将其集成到视觉回归测试工作流中,以对比快照,而不是依赖本地 Web 服务器。
这种方法有两个好处:
- 避免为运行测试而启动本地 Web 服务器。
- 使用预览链接运行测试会生成更可靠的快照,因为这些链接与生产环境的展示内容一致。
从总体上看,要使 Playwright 测试支持部署预览,我们需要对代码库进行以下三项主要更改:
- 允许通过参数传递测试 URL,使测试能够识别部署预览链接。
- 更新 GitHub Action 工作流,等待部署预览完成。
- 将部署预览的 URL(作为环境变量)传递给 Playwright。
以下是如何在 Netlify 中实现(如果你使用的是 Vercel 或其他支持部署预览的平台,所需更改几乎相同)。
首先,在 playwright.config.ts
中更新 use.baseURL
的值,使其能够通过环境变量(WEBSITE_URL
)接收部署 URL:
1 | use: { |
此外,如果提供了 WEBSITE_URL
环境变量,禁用 Playwright 的 Web 服务器:
1 | webServer: process.env.WEBSITE_URL |
接下来,更新 playwright.yaml
工作流,以便针对部署预览运行测试。
要等待 Netlify 的部署预览 URL,我们可以使用 mmazzarolo/wait-for-netlify-action
GitHub Action。
mmazzarolo/wait-for-netlify-action
是 probablyup/wait-for-netlify-action
的一个分支。默认情况下,probablyup/wait-for-netlify-action
假定在由拉取请求推送触发的工作流中运行。而我们的 update-snapshots.yml
工作流是由评论触发的,因此我分叉了这个 GitHub Action,以确保它可以在任何工作流中运行,无论触发方式如何。
wait-for-netlify-action
GitHub Action 需要两项配置:
- 设置一个
NETLIFY_TOKEN
GitHub Action 密钥,使用 Netlify 个人访问令牌。 - 将 Netlify 站点 ID(Netlify 中:设置 → 站点详情 → 通用)传递给工作流中的
site_id
参数。
以下是 playwright.yaml
示例:
1 | name: Playwright Tests |
最后,像上面一样更新 update-snapshots.yml
工作流,确保 Playwright 的视觉回归测试可以针对部署预览运行。
结论
希望这篇博客文章为你搭建视觉测试体系提供了一个坚实的基础。以下是一些可以进一步改进的思路:
- 你可能希望通过使用
fullScreen
参数测试全屏快照,而不仅仅是可见视口。此外,还可以尝试捕获移动端的快照。 - 如果你的 Web 应用会异步加载部分 UI(例如图片、视频),可能需要在测试中等待它们加载完成,或者将它们从测试中排除。
- 可以限制
/update-snapshots
命令的权限,仅允许仓库的所有者调用。 - 你可以通过其他方式(例如 Webhooks)触发快照更新流程,而不是依赖 GitHub 评论。
- 将快照存储在第三方存储解决方案中可能是更灵活的选择。
- 如果你在使用部署预览,可以通过并行化等待预览链接的步骤与其他工作流步骤来优化工作流效率。