跳到主要内容

快照测试

概述

使用 Playwright 的快照测试功能,您可以将页面的无障碍访问树与预定义的快照模板进行比对断言。

await page.goto('https://playwright.dev/');
await expect(page.getByRole('banner')).toMatchAriaSnapshot(`
- banner:
- heading /Playwright enables reliable end-to-end/ [level=1]
- link "Get started"
- link "Star microsoft/playwright on GitHub"
- link /[\\d]+k\\+ stargazers on GitHub/
`);

断言测试 vs 快照测试

快照测试和断言测试在测试自动化中服务于不同的目的:

断言测试

断言测试是一种针对性方法,用于验证元素或组件的特定值或条件。例如,在 Playwright 中,expect(locator).toHaveText() 可以验证元素是否包含预期文本,而 expect(locator).toHaveValue() 则确认输入字段是否具有预期值。断言测试具有针对性,通常是将元素或属性的当前状态与预期的预定义状态进行比较。它们适用于可预测的单值检查,但在测试更广泛的结构或变化时范围有限。

优势

  • 清晰性:测试意图明确且易于理解
  • 针对性:测试专注于功能的特定方面,使其对无关变更更具鲁棒性
  • 调试便利:失败时能提供针对性反馈,直接指出问题所在

劣势

  • 复杂输出处理繁琐:为复杂数据结构或大型输出编写断言可能冗长且容易出错
  • 维护成本高:随着代码演进,手动更新断言可能耗时

快照测试

快照测试会捕获元素、组件或数据在特定时刻的完整状态"快照"或表示形式,并保存下来用于后续比较。当重新运行测试时,当前状态会与快照进行比对,如果存在差异,则测试失败。这种方法特别适用于复杂或动态结构的情况,因为手动断言每个细节会非常耗时。相比断言测试,快照测试的范围更广、更全面,能够追踪更复杂的变化。

优势

  • 简化复杂输出:例如,使用传统断言测试UI组件的渲染输出会非常繁琐。快照可以捕获整个输出以便轻松比较。
  • 快速反馈循环:开发者可以轻松发现输出中的意外变化。
  • 保持一致性:有助于在代码演进过程中维持输出的一致性。

劣势

  • 过度依赖:可能会在未完全理解变化的情况下接受快照变更,从而隐藏潜在错误。
  • 粒度问题:当出现差异时,大型快照可能难以解读,特别是当微小变化影响大部分输出时。
  • 适用性:不适合输出频繁或不可预测变化的高度动态内容。

使用场景

  • 快照测试适用于:
    • 完整页面和组件的UI测试
    • 复杂UI组件的整体结构检查
    • 结构很少变化的回归测试
  • 断言测试适用于:
    • 核心逻辑验证
    • 计算值测试
    • 需要精确条件的细粒度测试

通过将快照测试用于整体结构检查,断言测试用于特定功能验证,您可以实现全面的测试策略。

Aria 快照

在 Playwright 中,aria 快照提供了页面无障碍树的 YAML 表示形式。这些快照可以被存储并在后续进行比较,以验证页面结构是否保持一致或符合预期要求。

YAML 格式描述了页面上可访问元素的层级结构,详细说明了角色属性文本内容。该结构采用树状语法,其中每个节点代表一个可访问元素,缩进表示嵌套元素。

树中的每个可访问元素都表示为一个 YAML 节点:

- role "name" [attribute=value]
  • role: 指定元素的 ARIA 或 HTML 角色(例如 headinglistlistitembutton
  • "name": 元素的可访问名称。带引号的字符串表示精确值,/patterns/ 用于正则表达式
  • [attribute=value]: 方括号中的属性和值表示特定的 ARIA 属性,如 checkeddisabledexpandedlevelpressedselected

这些值来源于 ARIA 属性或基于 HTML 语义计算得出。要检查页面的无障碍树结构,请使用 Chrome DevTools 无障碍选项卡

快照匹配

Playwright 中的 expect(locator).toMatchAriaSnapshot() 断言方法会将定位器作用域的可访问性结构与预定义的 ARIA 快照模板进行比较,帮助验证页面状态是否符合测试要求。

对于以下 DOM 结构:

<h1>title</h1>

你可以使用以下快照模板进行匹配:

await expect(page.locator('body')).toMatchAriaSnapshot(`
- heading "title"
`);

在进行匹配时,快照模板会与页面当前的可访问性树进行比较:

  • 如果树结构匹配模板,则测试通过;否则测试失败,表明预期的可访问性状态与实际状态不匹配
  • 比较区分大小写并会折叠空白字符,因此缩进和换行会被忽略
  • 比较对顺序敏感,意味着快照模板中的元素顺序必须与页面可访问性树中的顺序一致

部分匹配

您可以通过省略属性或可访问名称来对节点进行部分匹配,从而能够验证无障碍树中的特定部分,而无需完全匹配。这种灵活性对于处理动态或不重要的属性非常有用。

<button>提交</button>

无障碍快照

- button

在这个例子中,按钮角色被匹配,但没有指定可访问名称("提交"),因此无论按钮标签是什么,测试都能通过。


对于具有 checkeddisabled 等 ARIA 属性的元素,省略这些属性可以实现部分匹配,仅关注角色和层级结构。

<input type="checkbox" checked>

部分匹配的无障碍快照

- checkbox

在这个部分匹配中,checked 属性被忽略,因此无论复选框状态如何,测试都会通过。


同样地,您可以通过省略特定的列表项或嵌套元素来部分匹配列表或组中的子元素。

<ul>
<li>功能 A</li>
<li>功能 B</li>
<li>功能 C</li>
</ul>

部分匹配的无障碍快照

- list
- listitem: 功能 B

部分匹配让您可以创建灵活的快照测试,验证页面的基本结构而不强制要求特定的内容或属性。

严格匹配

默认情况下,模板会匹配包含子集子元素的情况:

<ul>
<li>功能 A</li>
<li>功能 B</li>
<li>功能 C</li>
</ul>

部分匹配的 ARIA 快照

- list
- listitem: Feature B

可以使用 /children 属性来控制子元素的匹配方式:

  • contain(默认):如果所有指定的子元素按顺序存在则匹配
  • equal:如果子元素与指定列表完全按顺序匹配
  • deep-equal:如果子元素与指定列表完全按顺序匹配(包括嵌套子元素)
<ul>
<li>功能 A</li>
<li>功能 B</li>
<li>功能 C</li>
</ul>

由于模板中不包含功能 C,ARIA 快照将匹配失败

- list
- /children: equal
- listitem: Feature A
- listitem: Feature B

使用正则表达式匹配

正则表达式允许对具有动态或可变文本的元素进行灵活匹配。可访问名称和文本支持正则表达式模式。

<h1>Issues 12</h1>

使用正则表达式的 ARIA 快照

- heading /Issues \d+/

生成快照

在 Playwright 中创建 ARIA 快照有助于确保和维护应用程序的结构。根据您的测试设置和工作流程,可以通过多种方式生成快照。

使用 Playwright 代码生成器生成快照

如果您正在使用 Playwright 的 代码生成器,生成 ARIA 快照可以通过其交互式界面轻松完成:

  • "断言快照"操作:在代码生成器中,您可以使用"断言快照"操作为选定的元素自动创建快照断言。这是捕获 ARIA 快照作为测试流程记录的快捷方式。
  • "ARIA 快照"标签页:代码生成器界面中的"ARIA 快照"标签页会可视化展示选定定位器的 ARIA 快照,让您可以探索、检查和验证元素角色、属性及可访问名称,辅助快照的创建和审查。

使用 @playwright/test--update-snapshots 标志更新快照

当使用 Playwright 测试运行器 (@playwright/test) 时,您可以通过 --update-snapshots 标志(简写为 -u)自动更新快照。

使用 --update-snapshots 标志运行测试将更新所有不匹配的快照。匹配的快照不会被更新。

npx playwright test --update-snapshots

当应用程序结构变更需要新的快照作为基准时,更新快照非常有用。请注意,Playwright 会等待测试运行器配置中指定的最大 expect 超时时间,以确保在拍摄快照前页面已稳定。如果在生成快照时测试遇到超时,可能需要调整 --timeout 参数。

用于快照生成的空模板

在断言中传递空字符串作为模板可以即时生成快照:

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

请注意,Playwright 会等待测试运行器配置中指定的最大 expect 超时时间,以确保在拍摄快照前页面已稳定。如果在生成快照时测试遇到超时,可能需要调整 --timeout 参数。

快照补丁文件

当更新快照时,Playwright 会创建记录差异的补丁文件。这些补丁文件可以被审查、应用并提交到源代码管理系统中,使团队能够跟踪结构变化,并确保更新符合应用程序需求。

更新源代码的方式可以通过 --update-source-method 标志进行修改。有以下几种选项:

  • "patch"(默认):生成统一的 diff 文件,可以使用 git apply 应用到源代码。
  • "3way":在源代码中生成合并冲突标记,允许您选择是否接受更改。
  • "overwrite":直接用新的快照值覆盖源代码。
npx playwright test --update-snapshots --update-source-method=3way

将快照存储为单独文件

要将快照存储在单独文件中,可以使用 toMatchAriaSnapshot 方法配合 name 选项,并指定 .aria.yml 文件扩展名。

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

默认情况下,测试文件 example.spec.ts 的快照会存放在 example.spec.ts-snapshots 目录中。由于快照在不同浏览器中应该保持一致,即使使用多个浏览器进行测试,也只会保存一个快照。如果需要,您可以使用以下配置自定义快照路径模板

export default defineConfig({
expect: {
toMatchAriaSnapshot: {
pathTemplate: '__snapshots__/{testFilePath}/{arg}{ext}',
},
},
});

使用 Locator.ariaSnapshot 方法

locator.ariaSnapshot() 方法允许你以编程方式创建定位器范围内可访问元素的 YAML 表示,特别有助于在测试执行期间动态生成快照。

示例:

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

该命令会以 YAML 格式输出指定定位器范围内的 ARIA 快照,你可以根据需要验证或存储这些数据。

无障碍树示例

带级别属性的标题

标题可以包含 level 属性来指示它们的标题级别。

<h1>标题</h1>
<h2>副标题</h2>

ARIA 快照

- heading "标题" [level=1]
- heading "副标题" [level=2]

文本节点

独立或描述性文本元素会显示为文本节点。

<div>示例无障碍名称</div>

ARIA 快照

- text: 示例无障碍名称

内联多行文本

多行文本(如段落)在 ARIA 快照中会被规范化处理。

<p>第一行<br>第二行</p>

ARIA 快照

- paragraph: 第一行 第二行

链接

链接会显示其文本或由伪元素组成的文本内容。

<a href="#more-info">了解更多无障碍信息</a>

ARIA 快照

- link "了解更多无障碍信息"

文本框

text 类型的输入框会显示其 value 属性内容。

<input type="text" value="输入您的姓名">

ARIA 快照

- textbox: 输入您的姓名

带项目的列表

有序和无序列表会包含它们的列表项。

<ul aria-label="主要特性">
<li>特性1</li>
<li>特性2</li>
</ul>

ARIA 快照

- list "主要特性":
- listitem: 特性1
- listitem: 特性2

分组元素

分组(Groups)用于捕获嵌套元素,例如带有摘要内容的 <details> 元素。

<details>
<summary>Summary</summary>
<p>Detail content here</p>
</details>

aria 快照

- group: Summary

属性与状态

常用的 ARIA 属性如 checkeddisabledexpandedlevelpressedselected 用于表示控件状态。

带有 checked 属性的复选框

<input type="checkbox" checked>

aria 快照

- checkbox [checked]

带有 pressed 属性的按钮

<button aria-pressed="true">Toggle</button>

aria 快照

- button "Toggle" [pressed=true]