首页 / Playwright 入门教程 / 快照测试 Snapshot Testing

Playwright 入门教程

快照测试 Snapshot Testing

本教程共 59 篇 · 第 49 篇 · 更新于 2026-08-04 · 约 8 分钟阅读

Playwright快照测试SnapshotTestingAriaSnapshot无障碍树结构对比回归测试

本节目标:学完你能用一份「无障碍树快照」锁住页面结构,以后结构一变测试就报警。

上一章比的是「像素」。这章比的是「结构」。

做法是用 Aria 快照(aria snapshot)把页面的无障碍树(accessibility tree)存成一份模板。之后每次跑测试都拿它对一遍,结构乱了就报错。

Aria 快照是什么

无障碍树是浏览器给辅助技术(比如读屏软件)看的那棵树。它把页面抽象成「角色 + 名称 + 状态」。

Aria 快照就是这棵树的 YAML 文本表示。长这样:

await page.goto('https://playwright.dev/');

await expect(page).toMatchAriaSnapshot(`
  - banner:
    - heading /Playwright enables reliable end-to-end/ [level=1]
    - link "Get started":
      - /url: /docs/intro
`);

页面级用 expect(page).toMatchAriaSnapshot(),只想比某一块就用 expect(locator).toMatchAriaSnapshot()

断言测试 vs 快照测试

两种思路不一样,别混为一谈。

断言测试像点名:你逐条查某个值。toHaveText() 查文字,toHaveValue() 查输入框值。它精准、好定位,但结构复杂时写起来啰嗦。

快照测试像拍全景:把整块结构一次存下来,以后整体比。适合整页、整组件这种「大体不变」的东西。

代价是粒度粗。快照一大,报错时你得自己在 diff 里找哪一行变了。

Tip

实战里常搭配用:结构用快照锁住,关键数值用断言查。粗中有细。

快照长啥样

每个节点写法固定:角色 "名称" [属性=值]

- heading "title"
- button "Submit"
- checkbox [checked]
  • role:元素角色,如 headinglistlistitembutton
  • “name”:可访问名称。引号里是精确值,/正则/ 是模糊匹配。
  • [属性]checkeddisabledexpandedinvalidlevelpressedselected 这些状态。

想看真实的无障碍树,打开 Chrome DevTools 的 Accessibility 面板,比自己猜快得多。

匹配的三条规则

新手最容易在这三条上翻车,先记住:

  1. 大小写敏感"Submit""submit" 不是一回事。
  2. 空白会被折叠。缩进和换行不影响结果,放心排版。
  3. 顺序敏感。模板里节点的先后,必须跟页面无障碍树里的先后一致。

完全匹配与部分匹配

最省心的是部分匹配:只写你关心的节点,其余忽略。

<button>Submit</button>
- button

只写 button,名字无所谓,测试照样过。这对会变的文案很友好。

属性也能省。<input type="checkbox" checked> 写成 - checkbox,勾没勾都能过。

列表同理,可以只盯某一项:

- list
  - listitem: Feature B

上面的写法只要求列表里「有 Feature B」这一项,前后还有啥不管。

控制子节点的匹配强度

/children 决定子节点要匹配多严:

  • contain(默认):模板里的子节点按顺序都在就行。
  • equal:子节点要完全对上,不能多不能少。
  • deep-equal:连嵌套子节点也要完全对上。
- list
  - /children: equal
  - listitem: Feature A
  - listitem: Feature B

页面里要是还有个 Feature C,上面这份模板就会失败——因为你要求了 equal

想全项目默认用 equal,写进配置:

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

export default defineConfig({
  expect: {
    toMatchAriaSnapshot: { children: 'equal' },
  },
});

单个快照仍可以用 /children 覆盖全局设置。

用正则对付动态文字

数字、时间戳这类用正则兜住:

- heading /Issues \d+/

链接地址也能用正则,写在 /url 里:

- link:
  - /url: /https:\/\/example\.com\/.*/

怎么生成快照

三条路,从懒到勤。

第一条,空模板现场生成。 传空字符串,Playwright 会把当前结构填回你的源码里。

await expect(locator).toMatchAriaSnapshot('');

第二条,跑测试时整体刷新。 -u--update-snapshots 的简写。

npx playwright test --update-snapshots

只有不匹配的快照会被更新,已经对上的不动。更新时 Playwright 会先等到 expect 超时,确保页面稳定;页面慢的话适当调大 --timeout

默认直接覆盖基准快照文件。改版后先跑一次,用 git diff 确认差异是自己想要的再提交:

npx playwright test --update-snapshots

第三条,用 codegen 录。 录制工具栏里有「Assert snapshot」动作,点一下就把当前选中元素的快照断言写进来;旁边的「Aria snapshot」标签页还能实时看某个定位器的树长什么样。

快照存成独立文件

模板长了塞在代码里很难读,可以存成 .aria.yml

await expect(page.getByRole('main')).toMatchAriaSnapshot({ name: 'main.aria.yml' });

默认放在 example.spec.ts-snapshots/ 目录下。结构在各浏览器里是一样的,所以多浏览器跑也只存一份。

想在代码里拿到快照文本

page.ariaSnapshot()locator.ariaSnapshot() 直接返回 YAML 字符串,适合自己做处理:

const snapshot = await page.ariaSnapshot();
console.log(snapshot);

调试「为什么模板对不上」时,把它打出来跟模板肉眼比一遍,最快。

我的用法

我一般拿它守住「页面骨架」——导航在、主区域在、关键按钮在。具体文字和数值再用断言查。

这样既防结构被改坏,又不会因为一句文案变动就误报。

一句话:Aria 快照比的是结构不是像素,适合锁整页整组件的骨架,动态内容用正则和部分匹配兜底。

小结

Aria 快照比的是结构不是像素,适合锁整页整组件的骨架。动态文字用正则兜住,children 设 equal 能严格控子节点。生成快照三条路——空模板现场填、-u 整体刷、codegen 录。调试时把 ariaSnapshot() 打出来跟模板肉眼比,最快定位问题。