教程
Shotomatic Team
约 13 分钟阅读

如何用 Playwright 做截图测试

使用视觉基准、浏览器项目、稳定等待、差异阈值和更新审查,构建 Playwright 截图测试。

笔记本电脑上显示 Playwright 截图测试流程代码

Playwright 截图测试用于在 UI 变化发布前发现问题。测试会打开页面、等待稳定状态、截图,再与已批准的基准图比较。这正是普通自动截图与视觉回归测试的区别。

本文示例使用 Shotomatic 功能页 https://www.shotomatic.com/features 作为公开目标。同样的模式在自己的应用中更有价值,因为你可以控制数据、登录状态、动画和测试选择器。

说明:我们开发了 Shotomatic,因此示例使用我们的公开功能页。Playwright 用法来自当前官方文档。

如果只需要从一组 URL 生成图片文件,请先阅读普通 Playwright 自动截图。本文讲的是测试版本:基准图、截图对比、浏览器项目和审查。

何时值得使用截图测试

如果视觉错误很容易被普通断言漏掉,截图测试就很有用。

适合的对象:

  • 主视觉区、价格区或注册区很重要的营销页面;
  • 有多种状态的设计系统组件;
  • 版式很重要的结账、引导或仪表盘界面;
  • 经常在移动宽度下出错的响应式页面;
  • CSS 变化可能悄悄移动或隐藏内容的页面。

不太适合的对象:

  • 信息流、广告、时间戳或用户内容不断变化的页面;
  • 视觉细节并不重要的页面;
  • 已经由更可靠的文字、角色或行为断言覆盖的流程;
  • 没有审查流程,却包含数百个不稳定页面的整站。

从少量重要界面开始。让截图测试监视所有内容,很快就会产生大量噪声。

安装 Playwright Test

在新的或现有 Node 项目中,Playwright 官方安装器可以创建测试运行器、配置文件、示例测试和浏览器安装步骤:

$ npm init playwright@latest

安装器会询问使用 TypeScript 还是 JavaScript、测试文件放在哪里、是否添加 GitHub Actions 工作流,以及是否安装浏览器。设置完成后运行测试:

$ npx playwright test

本文示例使用 tests/ 目录中的 TypeScript 文件。

创建第一个截图测试

创建 tests/features-page.spec.ts

import { expect, test } from "@playwright/test";

test("features page has a stable desktop layout", async ({ page }) => {
  await page.goto("https://www.shotomatic.com/features");

  await expect(
    page.getByRole("heading", {
      name: /Shotomatic Features/i,
    }),
  ).toBeVisible();

  await expect(page).toHaveScreenshot("features-page-desktop.png", {
    fullPage: true,
  });
});

page.screenshot() 的关键区别是断言。Playwright Test 的 toHaveScreenshot() 会创建基准图,或与现有基准比较。第一次运行会写入缺失的基准;之后运行则比较当前截图和已保存图片。

运行测试:

$ npx playwright test tests/features-page.spec.ts

首次运行时,由于基准图尚不存在,Playwright 会写入一张新的预期图片。提交前先检查它。基准图是测试预期,不是随手生成的产物。

测试页面的一小部分

全页截图能发现大范围版式偏移,但也更容易因为页面下方无关变化而失败。对许多团队来说,维护区块或组件基准更轻松。

import { expect, test } from "@playwright/test";

test("features comparison cards stay aligned", async ({ page }) => {
  await page.goto("https://www.shotomatic.com/features");

  const comparisonHeading = page.getByRole("heading", {
    name: /Choose a workflow/i,
  });
  const comparisonSection = page.locator("section").filter({
    has: comparisonHeading,
  });

  await expect(comparisonHeading).toBeVisible();
  await expect(comparisonSection).toHaveScreenshot("features-comparison-section.png");
});

如果审查问题只涉及局部——“这个区块看起来还正常吗?”——就用定位器截图。需要检查整页版式时,再用页面截图。

添加浏览器和设备项目

Playwright 测试运行器可以在多个命名项目中运行同一测试。截图测试由此不同于简单的捕获脚本:浏览器、设备、视口和快照名称都会成为测试矩阵的一部分。

playwright.config.ts 示例:

import { defineConfig, devices } from "@playwright/test";

export default defineConfig({
  testDir: "./tests",
  use: {
    baseURL: "https://www.shotomatic.com",
    trace: "on-first-retry",
  },
  projects: [
    {
      name: "desktop-chromium",
      use: {
        ...devices["Desktop Chrome"],
        viewport: { width: 1440, height: 1000 },
      },
    },
    {
      name: "mobile-safari",
      use: {
        ...devices["iPhone 13"],
      },
    },
  ],
});

随后在测试中使用相对 URL:

import { expect, test } from "@playwright/test";

test("features page visual baseline", async ({ page }) => {
  await page.goto("/features");
  await expect(page.getByRole("main")).toBeVisible();

  await expect(page).toHaveScreenshot("features-page.png", {
    fullPage: true,
  });
});

Playwright 会为不同项目保存不同快照,因此桌面与移动基准可以并列存在,不必假装两者应该相同。当问题是 UI 是否适配不同设备尺寸时,这比只截一个视口更合适。

让页面状态保持稳定

许多无意义的截图失败都来自不稳定页面状态。Playwright 默认会在截图断言期间禁用 CSS 动画和过渡,但变化中的数据、延迟内容、广告和用户专属元素仍会移动像素。

截图前等待一个具体条件:

await page.goto("/features");
await expect(page.getByRole("main")).toBeVisible();
await expect(page.getByText("Hands-Free Capture")).toBeVisible();

不要把固定超时当作主要等待规则。如果页面必须等某张卡片、标题、表格或图片加载完才算就绪,就直接等待该条件。

如果全页截图包含延迟加载内容,先滚动页面:

await page.evaluate(async () => {
  const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
  const viewportHeight = window.innerHeight;

  for (let y = 0; y < document.body.scrollHeight; y += viewportHeight) {
    window.scrollTo(0, y);
    await delay(100);
  }

  window.scrollTo(0, 0);
});

页面确实需要时,这类辅助函数没有问题。把它放在测试附近,让以后维护的人知道为什么要滚动。

隐藏或遮罩易变元素

页面的某些部分不应参加视觉比较,例如时间戳、头像、广告、用户名称、加载指示器或视频缩略图。

常用方法有两种。

遮罩指定定位器:

await expect(page).toHaveScreenshot("features-page.png", {
  fullPage: true,
  mask: [page.locator("[data-testid='release-date']")],
});

或在截图时应用样式表:

await expect(page).toHaveScreenshot("features-page.png", {
  fullPage: true,
  stylePath: "./tests/screenshot.css",
});

tests/screenshot.css 示例:

[data-testid="release-date"],
[data-testid="animated-cursor"] {
  visibility: hidden !important;
}

不要为了让测试通过而隐藏真正的产品 UI。遮罩只用于不属于视觉问题、却会产生噪声的内容。

谨慎设置差异阈值

Playwright 可以允许少量像素差异:

await expect(page).toHaveScreenshot("features-page.png", {
  fullPage: true,
  maxDiffPixels: 100,
});

这能容忍细小渲染差异,但也可能藏住真正的回归。阈值要低,并记录存在原因。放宽对比前,优先让页面更确定。

还可以在 playwright.config.ts 中共享截图断言默认值:

import { defineConfig } from "@playwright/test";

export default defineConfig({
  expect: {
    toHaveScreenshot: {
      maxDiffPixels: 100,
    },
  },
});

审查并更新基准

截图测试失败时,问题不是“怎样让它变绿”,而是“这次视觉变化是否符合预期”。

如果变化是 Bug,就修复 UI 后重新运行。如果变化是有意的,再更新基准:

$ npx playwright test --update-snapshots

然后在拉取请求中检查新的基准图。像对待代码改动一样对待快照更新:应有人理解改了什么、为什么改。

在 CI 中运行截图测试

截图比较对环境很敏感。字体、操作系统、浏览器版本、硬件和无头模式都可能影响像素。要得到稳定结果,请在相同环境中生成和比较基准。

实践中通常意味着:

  • 提交在 CI 或匹配本地容器中生成的基准快照;
  • 在拉取请求上运行截图项目;
  • 除非本来就接受差异,否则不要用 macOS 生成的基准与 Linux CI 比较;
  • 有计划地更新 Playwright 及其浏览器二进制文件;
  • 接受变化前先审查更改的快照。

失败难以理解时,使用 Playwright HTML 报告或 trace viewer。截图测试失败需要比“构建变红”更多的审查语境。

普通自动截图仍适用的场景

截图测试用于检查代码所控制的 UI 质量,不等于为审阅、报告、归档或内容流程收集截图。

需要从 URL 获取输出文件时,使用普通 Playwright 自动截图

需要一个专注 Chrome 的小脚本,并且不需要 Playwright Test 时,使用 Puppeteer 自动截图

任务是无需代码的 URL 列表捕获时,使用 Shotomatic Website Capture:添加 URL、调整捕获选项、检查结果并导出截图,无需维护测试套件。团队手动检查响应式截图时,这可能已经足够;需要自动判断通过/失败时,请继续使用 Playwright Test。

常见错误

  • 测试页面范围太大:先为重要区块创建基准,再考虑整页。
  • 捕获不稳定数据:对每次运行都会变化的内容使用固定测试数据、遮罩或样式。
  • 不经检查就更新快照:只有审查视觉差异后才更新基准。
  • 在一台机器生成基准、到另一台比较:保持渲染环境一致。
  • 把视觉测试当行为测试:截图之外,还要对文字、角色、导航和交互使用普通断言。

常见问题

Playwright 能做截图测试吗?

能。Playwright Test 通过 expect(page).toHaveScreenshot()expect(locator).toHaveScreenshot() 提供截图断言。

截图测试和保存截图有什么区别?

保存截图只会创建图片文件。截图测试会把新截图与已批准的基准图比较,差异超过允许阈值时测试失败。

Playwright 把截图基准存在哪里?

默认情况下,Playwright 将基准保存在以测试文件命名、带 -snapshots 后缀的目录中。可以在 Playwright 配置中用 snapshotPathTemplate 自定义路径。

如何更新 Playwright 截图基准?

确认视觉变化符合预期后,使用 --update-snapshots 运行 Playwright Test。

每个页面都应该使用 Playwright 截图测试吗?

不应该。截图测试适合重要且稳定的 UI 状态。行为应使用普通断言;视觉基准只用于版式变化很重要的页面或组件。

参考资料

以上示例按照当前官方文档检查:

相关文章

查看更多文章

如何用 Playwright 自动截取网站截图

使用 Playwright 和 JavaScript 自动截取网站截图,包括单页、整页、移动端视图,以及带重试机制的 URL 批量截图。

约 18 分钟阅读
笔记本电脑上显示网站自动截图脚本代码

如何用 Puppeteer 自动截取网站截图

使用 Puppeteer 和 JavaScript 自动截取网站截图。通过可重复使用的脚本截取单页、整页、URL 批次和移动端视口。

约 18 分钟阅读
笔记本电脑上显示 Puppeteer 网站截图脚本代码

如何不写代码自动截取网站截图

选择无需代码的网站截图自动化方式:URL 列表截图、自动化平台配合截图 API、监控工具或浏览器辅助工具。

约 12 分钟阅读
用于检查网站截图工作流的桌面多显示器

2026 年适用于 Mac 的 Tango 替代工具

从本地指南制作、托管式工作区、桌面采集、应用内引导、视频和导出等方面,对比适用于 Mac 的 Tango 替代工具。

约 7 分钟阅读
一位正在 Mac 上记录桌面工作流的人

无需编写代码,自动截取网页

Website Capture 可以截取 URL 列表,为每个页面设置选项,检查结果并导出,不需要维护脚本。

如何用 Playwright 做截图测试 | 博客 | Shotomatic