手机端视觉回归测试:免费的Playwright设置

视觉回归测试:用Playwright的toHaveScreenshot免费设置手机视口测试,修复不稳定的像素差异,并比较主流工具。

目录

视觉回归测试会把页面截图与已批准的基准图像进行比较,像素发生变化时测试失败。对于移动端布局,它能发现功能测试漏掉的问题:菜单按钮被挤出360px的屏幕、标题折成三行、卡片网格在平板宽度下错误地塌陷。你可以用Playwright的toHaveScreenshot()在手机和平板视口下免费运行它;之后再判断,对于审核流程以及云端的跨浏览器渲染,付费服务是否值得。

视觉回归测试如何工作

一次视觉测试分为四步:捕获基准图,在改动后再次捕获,比较两张图片的差异,然后由人接受或拒绝这些差异。被接受的改动会成为新的基准。

不同工具的差异主要体现在比较方式上。像素比较(Playwright、BackstopJS)统计变化的像素,并在超过你设定的阈值时失败。Applitools使用自己的Visual AI进行比较,而不是严格的像素计数;Percy和Chromatic是带审核面板的托管服务。它们都需要页面稳定:如果两次运行之间时钟、广告或动画发生了变化,测试就会在没有真实原因的情况下失败。下面的大部分设置工作,都是在消除这些噪声。

在 360 px 宽的演示页面上做视觉回归测试:基准图、导航变长后 Menu 按钮被挤到边缘的新截图,以及用红色标出变化像素的差异图

工具比较

Playwright内置的断言适用于免费的自托管场景。截至2026年10月:

工具 差异比较方式 费用 优势所在
Playwright toHaveScreenshot 本地像素比较 免费,开源 一个运行器覆盖Chromium、Firefox和WebKit,基准图保存在你的代码仓库中
BackstopJS 本地像素比较 免费,开源 仅通过配置定义场景(无需测试代码),HTML报告,支持--docker参数
Percy(BrowserStack) 云端比较与审核界面 免费套餐:每月5,000张截图 托管的审核和批准流程,覆盖多种浏览器和宽度
Chromatic 云端比较,基于Storybook 免费套餐:每月5,000个快照,仅Chrome 适用于Storybook项目的组件级测试
Applitools Eyes Visual AI 入门版每月667美元起,按年计费;提供免费试用 旨在忽略像素差异所标记的渲染噪声的AI比较;大规模跨浏览器网格

Percy会把每个页面在每种浏览器和每个宽度下计为一张截图,因此两个页面在三种手机和平板宽度下、两种浏览器中,就已经用掉了12张。套餐价格和限额来自各供应商在2026年10月25日的定价页面,且经常变动。

在手机视口下设置视觉回归测试

这套设置使用@playwright/test 1.64(2026年10月25日的当前版本)。它需要Node.js,以及一个可以从测试机器打开的网站,本地或已部署的都可以。

  1. 安装Playwright及其浏览器:

    npm init playwright@latest
  2. 在playwright.config.ts中添加移动端项目。每个设备描述符会设置视口、像素比、用户代理和触摸支持,并选择引擎(iPhone 15在WebKit中运行,Pixel 7在Chromium中运行)。

    import { defineConfig, devices } from '@playwright/test';
    
    export default defineConfig({
      testDir: './visual',
      use: { baseURL: 'http://localhost:4321' },
      expect: {
        toHaveScreenshot: { maxDiffPixelRatio: 0.01 },
      },
      projects: [
        { name: 'pixel-7', use: { ...devices['Pixel 7'] } },
        { name: 'iphone-15', use: { ...devices['iPhone 15'] } },
        { name: 'tablet', use: { browserName: 'chromium', viewport: { width: 820, height: 1180 } } },
      ],
    });

    在Playwright 1.64中,Pixel 7描述符的视口为412 × 839,像素比为2.625;iPhone 15的视口为393 × 659,像素比为3。1%的maxDiffPixelRatio只是一个你需要调整的起始值,并非标准。

  3. 编写测试。只针对布局最重要的页面:

    // visual/pages.spec.ts
    import { test, expect } from '@playwright/test';
    
    const pages = ['/', '/pricing', '/blog'];
    
    for (const path of pages) {
      test(`layout ${path}`, async ({ page }) => {
        await page.goto(path);
        await page.evaluate(async () => { await document.fonts.ready; });
        await expect(page).toHaveScreenshot({
          fullPage: true,
          mask: [page.locator('[data-testid="live-date"]')],
        });
      });
    }
  4. 创建基准图并提交:

    npx playwright test --update-snapshots

    Playwright为每个测试和每个项目保存一张PNG,文件名末尾带有项目名和平台(例如...-iphone-15-linux.png)。请提交*-snapshots文件夹。

  5. 在每个拉取请求上运行npx playwright test。比较失败会使测试失败;用npx playwright show-report打开HTML报告查看失败的地方。如果改动是预期的,就用--update-snapshots更新基准图,并与代码改动一起提交。

默认设置在这里很有帮助:toHaveScreenshot()会等到两次连续的截图一致后才进行比较,禁用CSS动画和过渡,并隐藏文本光标(Playwright API文档)。它的scale选项默认为"css",即每个CSS像素对应一个图像像素,因此iPhone的基准图宽度为393 px而不是1179,差异比较也更快。

在拍摄基准图之前先查看布局

基准图记录的是你拍摄时页面的样子。如果第一张截图的手机布局已经有问题,那么之后的每次运行都会把这个缺陷“保护”下来。在创建基准图之前,请在你要测试的每个宽度下查看页面:

  1. 在手机宽度下打开页面。Mobile View Extension会以手机的用户代理、屏幕尺寸和像素比重新加载标签页,并且可以并排显示两种设备,因此你可以同时检查同一URL的手机和平板布局。它可以在localhost上运行。
  2. 滚动整个页面,检查导航、换行的标题、表格和粘性元素。
  3. 修复发现的问题,然后运行--update-snapshots。

该扩展使用的是桌面版Chrome引擎和鼠标,因此它展示的是布局,而不是Safari的渲染或触摸行为。Playwright中的WebKit项目能弥补其中一部分差距,真正的iPhone则覆盖其余部分。手动检查的更完整流程见如何测试响应式设计,为工单做一次性截图的方法见如何为网站截取手机截图。

修复不稳定的截图差异

不稳定的差异来自每次运行之间会变化的东西,因此修复方法是让这些东西每次都相同。以下是常见来源及对应的处理办法:

原因 修复方法
操作系统或浏览器版本不同 在相同类型的机器上生成并比较基准图;在CI中,使用与Playwright版本匹配的固定Docker镜像,例如mcr.microsoft.com/playwright:v1.64.0-noble
网页字体加载晚或回退 等待document.fonts.ready;自托管字体,使CDN延迟不会改变渲染结果
日期、计数器、广告、嵌入内容 用mask覆盖对应的定位器,或通过stylePath样式表隐藏它们
轮播图和JavaScript动画 用仅供测试使用的标志停止它们;toHaveScreenshot只会冻结CSS和Web Animations
悬停状态 先把鼠标移开:page.mouse.move(-1, -1)
整页截图中的懒加载图片 在截图前滚动到底部,或等待图片加载完成
抗锯齿噪声 提高threshold(默认0.2),或设置maxDiffPixels

第一行解释了看起来像随机的失败。Playwright的文档警告,渲染结果可能因宿主操作系统、版本、设置和硬件而异,正因如此,平台被包含在截图名称中:在Mac上创建的基准图(-darwin.png)不会被Linux CI运行器(-linux.png)使用。为基准图选定一个环境。在与CI使用的相同Docker镜像中运行npx playwright test --update-snapshots,可以消除这一差异来源。

对于在所有测试中隐藏噪声,stylePath文件是最干净的地方:

/* visual/screenshot.css */
iframe, [data-testid="cookie-banner"] { visibility: hidden; }
import path from 'node:path';

await expect(page).toHaveScreenshot({
  stylePath: path.join(__dirname, 'screenshot.css'),
});

同样的选项也可以写在配置文件的expect.toHaveScreenshot中,以应用于每个测试。

视觉测试无法发现的问题

截图比较只检查你所运行的引擎中的像素。它无法检查按钮能否正常工作、点击目标是否足够大,也无法检查页面在真实手机浏览器的界面和键盘下的表现。Playwright的WebKit接近Safari,但并不等同于iPhone上的iOS Safari。请把功能测试与视觉测试放在一起,并使用跨浏览器测试清单覆盖真实浏览器。

让测试套件保持精简。每个页面、项目和状态都会成倍增加需要审核的图片数量。三到十个关键模板(首页、列表页、详情页、表单)在两种手机尺寸和一种平板尺寸下测试,每次运行只需审核几十张图片。这个数量只是一个可以调整的起点(属于设计上的选择),并非规则。

常见问题

视觉回归测试是免费的吗?

是的,使用开源工具即可。Playwright的toHaveScreenshot()和BackstopJS都可以在本地和你的CI中免费运行。Percy、Chromatic和Applitools等付费服务增加了托管渲染和审核面板;截至2026年10月,Percy和Chromatic的免费套餐每月提供5,000张截图或快照。

视觉测试和截图测试有什么区别?

两者都会截图,并与已批准的基准图进行比较。区别在于比较方式:Playwright和BackstopJS统计变化的像素,而Applitools使用Visual AI(见上面的工具表)。

为什么我的Playwright截图在CI中失败,本地却通过?

因为基准图是在不同的操作系统或浏览器版本上生成的。Playwright会把平台包含在截图文件名中,而且macOS和Linux上的字体和抗锯齿也不同。请在CI使用的同一个Docker镜像中创建基准图。

如何在视觉测试中忽略动态内容?

使用mask选项覆盖日期和广告等元素,或通过stylePath样式表将其隐藏。对于会变化的整块区域,可以只截取一个稳定元素,而不是整页。

用手机视图查看你的下一个网站。

添加扩展,在你正在使用的标签页中直接打开设备预览。