Playwright自动化视觉测试

1,384 阅读5分钟

提前阅读

视觉测试 Visual Testing

主要是有能力检查用户界面的图像是否正确,视觉测试也是E2E测试的一种测试手段。之前接触到自动化测试框架,基本都是核对文本来做校验,或者是类文本的对比,那么视觉测试就是可以校验图片,或者是二进制文件。

视觉比较 Visual Comparisons

Playwright新版支持了快照(Snapshots)比较的断言:

  • await expect(page).toHaveScreenshot();
  • expect(await page.screenshot()).toMatchSnapshot('my-page.png');

所以比较截图的快照就能进行自动化视觉测试了,但Playwright也有一些与众不同的设计。

页面截图比较

先看一个最典型的例子

// example.spec.ts
import { test, expect } from '@playwright/test';

test('example test', async ({ page }) => {
  await page.goto('https://playwright.dev');
  await expect(page).toHaveScreenshot();
});

await expect(page).toHaveScreenshot() 这个断言就是先对page进行截图,然后与设定好的快照截图进行比较。默认没有任何参数就是:

  • 首先截图是默认截取当前页面可视区域,可能不是整个页面
  • 然后快照是读取默认位置默认名称的快照。
    • 默认位置在spec文件同一级的目录,比如example.spec.ts-snapshots,spec文件名加上-snapshots后缀的目录,也就是testInfo.snapshotDir
    • 默认名称是当前测试的title,加上浏览器名,再加上系统名。比如example-test-1-chromium-darwin.png。之所以这样是因为不同的浏览器和操作系统的字体可能不同,这样相同的页面渲染的图像界面也可能不同,为了保障像素级别的比较有意义,快照必须针对不同的系统和浏览器进行严格匹配。如果不想用title作为名称前缀,可以指定参数自定义名称:
await expect(page).toHaveScreenshot('my-snapshot-name.png');

到这里一定会有一个疑问,快照文件从哪来?如何产生快照文件?

Playwright自动产生快照文件

对于截图的快照,Playwright是支持自动产生的:

  • 第一次运行测试的时候,大概是开发阶段,playwright如果发现没有快照文件,会自动生成对应的快照文件到对应的位置,当然此刻的测试肯定是failed。
  • 一旦快照产生后,后面的测试运行就可以进行比较了。所以还需要把快照文件夹example.spec.ts-snapshots提交到git仓库才行,以支持后续的运行。注意不同的环境文件名会不同,所以开发的时候如果你的本地是Windows,回归测试的在服务端用的是Linux,那么此时测试快照其实是缺失的,需要想办法对应到。比如可以使用虚拟机在本地运行一次并提交对应的快照到git仓库
  • playwright还提供一种方式,相当于重置所有快照。产生后也记得提交到git仓库。
npx playwright test --update-snapshots

容错对比

Playwright支持截图对比的时候,不一定要求100%匹配,可以通过参数允许一定量的不匹配。

await expect(page).toHaveScreenshot({ 
    maxDiffPixelRatio: 0.1,
    maxDiffPixels: 100
});

这里有两个参数,使用一种即可:

  • maxDiffPixelRatio 0-1之间,0.1就是允许10%的不匹配像素,否则失败
  • maxDiffPixels 像素的数量,100就是允许100个不匹配像素,否则失败

例如,在错误日志里可以看到像素的不匹配信息

Error: Screenshot comparison failed:  
  
4510 pixels (ratio 0.01 of all image pixels) are different.  
  
Call log:  
- expect.toHaveScreenshot with timeout 5000ms  
- verifying given screenshot expectation  
- taking page screenshot  
- disabled all CSS animations  
- 4510 pixels (ratio 0.01 of all image pixels) are different.  
- waiting 100ms before taking screenshot  
- taking page screenshot  
- disabled all CSS animations  
- captured a stable screenshot  
- 4510 pixels (ratio 0.01 of all image pixels) are different.  
  
Expected: /monocart-reporter/image-comparison-Desktop-Chromium/my-snapshot-name-1-expected.png  
Received: /monocart-reporter/image-comparison-Desktop-Chromium/my-snapshot-name-1-actual.png  
Diff: /monocart-reporter/image-comparison-Desktop-Chromium/my-snapshot-name-1-diff.png

这里有4510 pixels,也就是1% (0.01)的像素不匹配,可以根据情况调整容错参数。同时还可以设置全局的容错参数,这样不用每个方法都添加:

// playwright.config.ts
import { defineConfig } from '@playwright/test';  
export default defineConfig({  
    expect: {  
        toHaveScreenshot: { 
            maxDiffPixels: 100 
        }
    }
});

视觉测试报告

除了从控制台错误日志看到比较的报告,还可以从官方的html报告更好的查看对比报告

image.png

也可以从第三方报告比如monocart-reporter更直观的查看对比报告

image.png

范围对比

最开始的例子是对比页面可视区域,可以通过fullPage参数对比完整的页面(也就是页面滚动条所有的区域)

await expect(page).toHaveScreenshot('my-snapshot-name.png', {
    fullPage: true
});

toHaveScreenshot断言除了支持page也支持到了locator,所以也可以对比页面上某个元素,比如一个按钮,一个div等等,直接使用locator的API定位选择即可

const locator = page.getByRole('button');  
await expect(locator).toHaveScreenshot();

蒙版比较

如果想在截图中排除一些区域不比较,通过蒙版参数mask可以实现:

await expect(page).toHaveScreenshot('my-snapshot-name.png', {
    fullPage: true,
    mask: [page.getTestId('name')]
});

这里假设页面有一个table,其中有一列是name,但并不想比较name这一列,即使它可能会不同,那么可以使用mask进行排除,这样截图里面就变成洋红区域,也就忽略了不同的比较,因为大家都是一片洋红。

image.png

注意mask参数是数组,所以就可以排除任意多个你想要排除的区域,就比较一个UI的外边框也是一样可以实现。如果不喜欢洋红色可以使用maskColor参数自定义遮罩颜色。

非图片快照比较

和截图比较是一样的,设置对应的快照名称即可,比如想要比较页面的一处的文本

expect(await page.textContent('.some-content')).toMatchSnapshot('my-content.txt');

扩展阅读