Playwright Test 注解与标签实战指南:Annotations、Tags 与条件跳过机制
2026/9/7 4:27:19 网站建设 项目流程

Playwright Test 注解与标签实战指南:Annotations、Tags 与条件跳过机制

【免费下载链接】playwrightPlaywright is a framework for Web Testing and Automation. It allows testing Chromium, Firefox and WebKit with a single API.项目地址: https://gitcode.com/GitHub_Trending/pl/playwright

本文基于 Playwright 官方文档 test-annotations-js.md 展开,系统讲解 Playwright Test 的标签(tags)与注解(annotations)体系:从内置的skip/fail/fixme/slow注解,到自定义 tag 的声明与--grep过滤,再到组级条件跳过、beforeEach中的fixme以及运行时注解。读完本文,你将能够用注解精确控制"哪些测试运行、哪些不运行、失败是否符合预期",并结合 testType.ts 等源码理解其底层实现。

内置注解:skip、fail、fixme、slow

Playwright 支持在测试报告(test report)中展示标签和注解。你可以随时添加自定义的 tag 和 annotation,而 Playwright 本身就内置了几个最常用的注解:

  • test.skip将测试标记为"无关",Playwright 不会执行该测试。适用于测试在某些配置(如某个浏览器、某个平台)下不适用的场景。
  • test.fail将测试标记为"预期失败"。Playwright 会照常执行该测试,并验证它确实失败;如果测试反而通过了,Playwright 会报错提醒你。
  • test.fixme同样将测试标记为失败状态,但与fail不同,Playwright 根本不会执行它。当测试执行很慢或直接崩溃时,用fixme把它挂起来。
  • test.slow将测试标记为慢测试,并将其超时时间乘以三倍。

注解可以加在单个测试上,也可以加在测试组(test.describe)上。内置注解支持条件形式——传入条件参数(truthy 时生效),条件还可以依赖测试 fixtures(如browserNameisMobile)。同一个测试上可以叠加多个注解,甚至可以来自不同的配置组合。

从源码结构看,这些注解本质上是类型受限的注解对象。test.ts 中定义了Modifier类型,其type字段只允许'slow' | 'fixme' | 'skip' | 'fail'四种取值,与文档列出的四个内置注解一一对应。而测试的预期结果(expected status)也在 test.ts 中根据注解推导:

if (annotation.type === 'skip' || annotation.type === 'fixme') this.expectedStatus = 'skipped'; else if (annotation.type === 'fail' && this.expectedStatus !== 'skipped') this.expectedStatus = 'failed';

这解释了文档中"skip 与 fixme 都不运行、fail 必须运行且必须失败"的行为差异:skip/fixme 使预期状态变为skipped,而 fail 使预期状态变为failed(若已被 skip/fixme 覆盖则保持 skipped)。

所有内置注解方法在 testType.ts 中统一注册到test对象上:

test.only = wrapFunctionWithLocation(this._createTest.bind(this, 'only')); test.skip = wrapFunctionWithLocation(this._modifier.bind(this, 'skip')); test.fixme = wrapFunctionWithLocation(this._modifier.bind(this, 'fixme')); test.fail = wrapFunctionWithLocation(this._modifier.bind(this, 'fail')); test.slow = wrapFunctionWithLocation(this._modifier.bind(this, 'slow'));

skipfixmefailslow共用同一个_modifier实现,这也意味着它们在"条件跳过"等场景下具有完全一致的调用形态。

聚焦测试:test.only

当你只想运行某个(或某些)测试时,可以用test.only聚焦。项目中只要存在被聚焦的测试,就只有这些测试会运行,整个项目范围内的其他测试都会被跳过:

test.only('focus this test', async ({ page }) => { // Run only focused tests in the entire project. });

从源码看,test.only在 testType.ts 中通过设置test._only = true标记实现,调度器据此过滤其余测试。

跳过单个测试:test.skip

最简单的跳过方式:

test.skip('skip this test', async ({ page }) => { // This test is not run });

条件跳过单个测试

在测试体内根据条件动态跳过。注意这里test.skip的第一个参数是条件(而非测试标题):

test('skip this test', async ({ page, browserName }) => { test.skip(browserName === 'firefox', 'Still working on it'); });

_modifier的源码(testType.ts)揭示了它的三种调用位置:

  1. describe块中传函数 —— 注册为_modifiers,加载阶段按条件求值;
  2. describe块中传条件(非 function)—— 条件为假时直接return,不产生注解,为真则推送静态注解;
  3. 在测试运行期间 —— 调用testInfo._modifier(...)对当前测试生效。

这也解释了为什么条件形式"可以依赖测试 fixtures":在测试体内调用时,_modifier直接作用于当前TestInfo实例。

用 test.describe 分组测试

test.describe给测试一个逻辑名称,也可以把 before/after 钩子的作用域限定在组内:

import { test, expect } from '@playwright/test'; test.describe('two tests', () => { test('one', async ({ page }) => { // ... }); test('two', async ({ page }) => { // ... }); });

给测试打标签(Tags)

当你想给测试打上@fast/@slow之类的标签,然后在测试报告中按标签过滤,或者只运行带某个标签的测试时,tags 就有用武之地。

给测试打标签有两种方式:声明测试时通过 details 对象提供tag,或者直接在测试标题中加入@前缀的 token。注意tag 必须以@符号开头

import { test, expect } from '@playwright/test'; test('test login page', { tag: '@fast', }, async ({ page }) => { // ... }); test('test full report @slow', async ({ page }) => { // ... });

也可以给整个组打标签,或一次提供多个标签:

import { test, expect } from '@playwright/test'; test.describe('group', { tag: '@report', }, () => { test('test report header', async ({ page }) => { // ... }); test('test full report', { tag: ['@slow', '@vrt'], }, async ({ page }) => { // ... }); });

声明后,可以用--grep命令行选项(见 test-cli.md 的 all-options 一节)只运行带特定标签的测试:

npx playwright test --grep @fast

PowerShell 下需要加引号:

npx playwright test --grep "@fast"

如果想反过来,跳过带某标签的测试,用--grep-invert

npx playwright test --grep-invert @fast

运行包含任一标签的测试(逻辑OR):

npx playwright test --grep "@fast|@slow"

运行同时包含两个标签的测试(逻辑AND,借助正则前瞻):

npx playwright test --grep "(?=.*@fast)(?=.*@slow)"

除了命令行,还可以在配置文件中通过grep配置项(TestConfig.grepTestProject.grep)对测试做过滤,适合把"只跑 @fast 子集"之类的策略固化到 playwright.config.ts 一类的配置文件里。

从源码结构看,标签与注解走的是同一套 details 校验管道:testType.ts 中validateTestDetails校验后,test.annotations.push(...validatedDetails.annotations)test._tags.push(...validatedDetails.tags)分别落位;组级 tags 则存在Suite._tags(test.ts 中注释明确区分了"显式声明的 tags"与"标题中解析出的 tags")。--grep的匹配对象正是由两部分标题与 tags 组合而成的检索路径(Suite._collectGrepTitlePath,test.ts),因此标题里的@slow与 details 里的tag: '@slow'--grep而言等价。

自定义注解(Annotations)

当你需要比 tag 更"有料"的信息时,可以使用注解。注解由typedescription组成,可以在 reporter API 中读取。Playwright 内置的 HTML 报告会展示所有注解,唯独不展示type_开头的注解——这是一个留给内部的命名空间,例如框架自身使用的元数据注解。

例如,用一个 issue URL 注解测试:

import { test, expect } from '@playwright/test'; test('test login page', { annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/23180', }, }, async ({ page }) => { // ... });

同样可以注解整个组,或一次提供多个注解:

import { test, expect } from '@playwright/test'; test.describe('report tests', { annotation: { type: 'category', description: 'report' }, }, () => { test('test report header', async ({ page }) => { // ... }); test('test full report', { annotation: [ { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/23180' }, { type: 'performance', description: 'very slow test!' }, ], }, async ({ page }) => { // ... }); });

条件跳过一组测试

test.skip/test.fixme回调函数即可实现组级条件跳过,例如让一组测试只在 Chromium 上运行:

test.describe('chromium only', () => { test.skip(({ browserName }) => browserName !== 'chromium', 'Chromium only!'); test.beforeAll(async () => { // This hook is only run in Chromium. }); test('test 1', async ({ page }) => { // This test is only run in Chromium. }); test('test 2', async ({ page }) => { // This test is only run in Chromium. }); });

对照_modifier的源码(testType.ts):在 describe 块中传入 function 时,它被压入suite._modifiers,等运行阶段按 fixture 参数求值后再决定是否给组内测试打上 skip/fixme 注解。

在 beforeEach 钩子中使用 fixme

如果连beforeEach钩子本身都不想让它执行(比如页面在移动端还没适配),可以把注解放进钩子内部:

test.beforeEach(async ({ page, isMobile }) => { test.fixme(isMobile, 'Settings page does not work in mobile yet'); await page.goto('http://localhost:3000/settings'); }); test('user profile', async ({ page }) => { await page.getByText('My Profile').click(); // ... });

条件成立时,当前测试被标记为 fixme(预期 skipped),钩子中test.fixme之后的语句不再有意义地影响结果。这对应_modifier的第三种调用形态:运行期间经testInfo._modifier(type, location, ...)作用于当前测试(testType.ts)。

运行时注解:test.info().annotations

测试已经在运行时,也可以动态追加注解,写入test.info().annotations

test('example test', async ({ page, browser }) => { test.info().annotations.push({ type: 'browser version', description: browser.version(), }); // ... });

这些注解会随TestInfo一起进入 reporter 数据(在 reporterTestRun.ts、teleEmitter.ts 等报告中随测试结果一并序列化),因此 HTML 报告、JSON 报告、JUnit 报告等都能拿到它们。

小结:注解体系如何落到源码

把文档行为与源码对应起来,可以得到一张清晰的映射表:

文档中的能力源码位置与机制
test.skip/fixme/fail/slow四内置注解testType.ts 统一经_modifier注册
test.only聚焦testType.ts 置test._only = true
skip/fixme/fail的标题式调用test.skip(title, body)testType.ts 转发到_createTest,并在 L120-L123 追加带location的注解
预期状态推导(skip/fixme → skipped,fail → failed)test.ts
组级条件注解(回调形式)suite._modifiers(test.ts 定义,testType.ts 写入)
自定义 tags/annotations 的声明校验validateTestDetails(testType.ts),分别落入TestCase.annotationsSuite._staticAnnotations与各级_tags

这套机制让 Playwright Test 的"选择运行哪些测试"完全数据化:静态注解在文件加载期就写入Suite._staticAnnotations/TestCase.annotations,动态注解在运行期经TestInfo落位,最终统一呈现在测试报告中——这也是注解(annotation)与标签(tag)虽然入口相似、语义却分层的根本原因:tag 服务于过滤--grepgrep配置),annotation 服务于表达(reporter API、HTML 报告展示)。

【免费下载链接】playwrightPlaywright is a framework for Web Testing and Automation. It allows testing Chromium, Firefox and WebKit with a single API.项目地址: https://gitcode.com/GitHub_Trending/pl/playwright

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询