最佳实践
简介
本指南应帮助您确保遵循我们的最佳实践,并编写更具弹性的测试。
测试理念
测试用户可见的行为
自动化测试应验证应用程序代码对最终用户是否有效,并避免依赖实现细节,例如最终用户通常不会使用、查看或甚至知道的东西,例如函数的名称,某些东西是否是数组,或某个元素的 CSS 类。最终用户将看到或与页面上渲染的内容进行交互,因此您的测试通常只应看到/与相同的渲染输出进行交互。
使测试尽可能隔离
每个测试都应该与其他测试完全隔离,并且应该独立运行,拥有自己的本地存储、会话存储、数据、cookie 等。测试隔离提高了可重复性,使调试更容易,并防止级联测试失败。
为了避免在测试的特定部分重复,您可以使用before 和 after 钩子。在您的测试文件中,添加一个 before 钩子,在每次测试之前运行测试的一部分,例如转到特定 URL 或登录到应用程序的一部分。这使您的测试保持隔离,因为没有测试依赖于另一个。但是,如果测试足够简单,尤其是在使测试更清晰、更易于阅读和维护的情况下,也有一些重复是可以接受的。
import { test } from '@playwright/test';
test.beforeEach(async ({ page }) => {
// Runs before each test and signs in each page.
await page.goto('https://github.com/login');
await page.getByLabel('Username or email address').fill('username');
await page.getByLabel('Password').fill('password');
await page.getByRole('button', { name: 'Sign in' }).click();
});
test('first', async ({ page }) => {
// page is signed in.
});
test('second', async ({ page }) => {
// page is signed in.
});
您还可以使用设置项目在测试中重用已签名的状态。这样,您只需登录一次,然后跳过所有测试的登录步骤。
避免测试第三方依赖项
只测试您控制的内容。不要尝试测试指向您无法控制的外部网站或第三方服务器的链接。这不仅耗时,还会减慢您的测试速度,而且您无法控制您链接到的页面的内容,或是否存在 cookie 横幅或覆盖页面或可能导致测试失败的任何其他内容。
相反,请使用Playwright 网络 API 并保证所需的响应。
await page.route('**/api/fetch_data_third_party_dependency', route => route.fulfill({
status: 200,
body: testData,
}));
await page.goto('https://example.com');
使用数据库进行测试
如果使用数据库,请确保您控制数据。针对暂存环境进行测试,并确保它不会改变。对于视觉回归测试,请确保操作系统和浏览器版本相同。
最佳实践
使用定位器
为了编写端到端测试,我们需要首先在网页上找到元素。我们可以通过使用 Playwright 内置的定位器 来做到这一点。定位器带有自动等待和可重试功能。自动等待意味着 Playwright 对元素执行一系列可操作性检查,例如确保元素在执行点击之前可见且已启用。为了使测试更具弹性,我们建议优先考虑面向用户的属性和明确的契约。
// 👍
page.getByRole('button', { name: 'submit' });
使用链接和过滤
定位器可以链接,以缩小搜索范围到页面的特定部分。
const product = page.getByRole('listitem').filter({ hasText: 'Product 2' });
您还可以过滤定位器,方法是按文本或按另一个定位器进行过滤。
await page
.getByRole('listitem')
.filter({ hasText: 'Product 2' })
.getByRole('button', { name: 'Add to cart' })
.click();
优先使用面向用户的属性而不是 XPath 或 CSS 选择器
您的 DOM 可以轻松更改,因此使测试依赖于 DOM 结构会导致测试失败。例如,考虑通过其 CSS 类来选择此按钮。如果设计师更改了一些内容,则该类可能会更改,从而破坏您的测试。
// 👎
page.locator('button.buttonIcon.episode-actions-later');
使用对 DOM 更改具有弹性的定位器。
// 👍
page.getByRole('button', { name: 'submit' });
生成定位器
Playwright 有一个测试生成器,可以为您生成测试并选择定位器。它将查看您的页面并找出最佳定位器,优先考虑角色、文本和测试 ID 定位器。如果生成器找到多个匹配定位器的元素,它将改进定位器,使其更具弹性并唯一识别目标元素,因此您不必担心由于定位器导致的测试失败。
使用 codegen
生成定位器
要选择定位器,请运行 codegen
命令,后跟您想要从中选择定位器的 URL。
npx playwright codegen playwright.dev
这将打开一个新的浏览器窗口以及 Playwright 检查器。要选择定位器,请首先单击“录制”按钮以停止录制。默认情况下,当您运行 codegen
命令时,它将开始新的录制。停止录制后,“选择定位器”按钮将可用,可以单击。
然后,您可以在浏览器窗口中将鼠标悬停在页面上的任何元素上,并看到光标下方突出显示的定位器。单击元素会将定位器添加到 Playwright 检查器中。您既可以复制定位器并将其粘贴到您的测试文件中,也可以通过在 Playwright 检查器中编辑它来继续探索定位器,例如通过修改文本,并在浏览器窗口中查看结果。
使用 VS Code 扩展生成定位器
您还可以使用VS Code 扩展 生成定位器以及录制测试。VS Code 扩展还为编写、运行和调试测试提供了出色的开发人员体验。
使用 Web优先断言
断言是一种验证预期结果和实际结果是否匹配的方法。通过使用Web优先断言,Playwright 将等待直到满足预期条件。例如,在测试警报消息时,测试将单击一个按钮使消息出现,并检查警报消息是否存在。如果警报消息需要半秒钟才会出现,则断言(例如 toBeVisible()
)将等待并在需要时重试。
// 👍
await expect(page.getByText('welcome')).toBeVisible();
// 👎
expect(await page.getByText('welcome').isVisible()).toBe(true);
不要使用手动断言
不要使用没有等待期望值的手动断言。在下面的代码中,await 在 expect 内部,而不是在 expect 之前。当使用断言(例如 isVisible()
)时,测试不会等待一秒钟,它只会检查定位器是否存在并立即返回。
// 👎
expect(await page.getByText('welcome').isVisible()).toBe(true);
请改用 Web优先断言,例如 toBeVisible()
。
// 👍
await expect(page.getByText('welcome')).toBeVisible();
配置调试
本地调试
对于本地调试,我们建议您在 VSCode 中实时调试测试。,方法是安装VS Code 扩展。您可以通过右键单击要运行的测试旁边的行来以调试模式运行测试,这将打开一个浏览器窗口并在断点设置的位置暂停。
您可以通过单击或编辑 VS Code 中测试中的定位器来实时调试测试,这将在浏览器窗口中突出显示此定位器,并向您显示在页面上找到的任何其他匹配定位器。
您还可以使用 Playwright 检查器调试测试,方法是使用 --debug
标志运行测试。
npx playwright test --debug
然后您可以逐步执行测试,查看操作日志并实时编辑定位器,并在浏览器窗口中看到突出显示。这将向您展示哪些定位器匹配,以及有多少个定位器匹配。
要调试特定测试,请添加测试文件名称和测试的行号,然后添加--debug
标志。
npx playwright test example.spec.ts:9 --debug
在 CI 上调试
对于 CI 失败,请使用 Playwright 跟踪查看器 而不是视频和屏幕截图。跟踪查看器为您提供了测试的完整跟踪,作为本地渐进式 Web 应用程序 (PWA),可以轻松共享。使用跟踪查看器,您可以查看时间线,使用开发工具检查每个操作的 DOM 快照,查看网络请求等等。
跟踪在 Playwright 配置文件中配置,并在失败测试的第一次重试时设置为在 CI 上运行。我们不建议将其设置为on
,这样跟踪就会在每次测试时运行,因为它非常占用性能。但是,您可以在使用--trace
标志进行开发时在本地运行跟踪。
npx playwright test --trace on
运行此命令后,将为每个测试记录跟踪,您可以直接从 HTML 报告中查看。
npx playwright show-report
可以通过单击测试文件名旁边的图标或打开每个测试报告并向下滚动到跟踪部分来打开跟踪。
使用 Playwright 的工具
Playwright 附带了一系列工具,可帮助您编写测试。
- VS Code 扩展 在编写、运行和调试测试时为您提供了极佳的开发人员体验。
- 测试生成器 可以为您生成测试并选择定位器。
- 跟踪查看器 为您提供了测试的完整跟踪,作为本地 PWA,可以轻松共享。使用跟踪查看器,您可以查看时间线,使用开发工具检查每个操作的 DOM 快照,查看网络请求等等。
- UI 模式 允许您使用时间旅行体验(包括监视模式)来探索、运行和调试测试。所有测试文件都将加载到测试侧边栏中,您可以在其中展开每个文件和描述块,以单独运行、查看、监视和调试每个测试。
- TypeScript 在 Playwright 中开箱即用,并为您提供更好的 IDE 集成。您的 IDE 将向您展示您可以执行的所有操作,并在您执行错误操作时突出显示。无需任何 TypeScript 体验,也不需要您的代码必须使用 TypeScript,您只需使用
.ts
扩展名创建您的测试即可。
跨所有浏览器测试
Playwright 使跨所有浏览器 测试您的站点变得容易,无论您使用的是什么平台。跨所有浏览器测试可确保您的应用程序对所有用户都有效。您可以在配置文件中设置项目,添加名称以及要使用的浏览器或设备。
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
],
});
保持 Playwright 依赖项更新
通过保持 Playwright 版本更新,您将能够在最新浏览器版本上测试您的应用程序,并在最新浏览器版本发布到公众之前发现错误。
npm install -D @playwright/test@latest
检查发行说明 以了解最新版本是什么以及发布了哪些更改。
您可以通过运行以下命令来查看您拥有的 Playwright 版本。
npx playwright --version
在 CI 上运行测试
设置 CI/CD 并定期运行您的测试。您运行测试的次数越多越好。理想情况下,您应该在每次提交和拉取请求时运行您的测试。Playwright 附带了一个GitHub 操作工作流,以便测试会在 CI 上为您运行,无需任何设置。Playwright 也可以在您选择的CI 环境 上进行设置。
在 CI 上运行测试时使用 Linux,因为它更便宜。开发人员可以在本地运行时使用任何环境,但在 CI 上使用 Linux。考虑设置分片 以加快 CI 速度。
对您的测试进行代码风格检查
我们建议使用 TypeScript 和 ESLint 对您的测试进行代码风格检查,以便尽早发现错误。使用@typescript-eslint/no-floating-promises
ESLint 规则,确保在 Playwright API 的异步调用之前没有缺少 await。在您的 CI 上,您可以运行tsc --noEmit
以确保函数使用正确的签名调用。
使用并行处理和分片
Playwright 默认情况下以并行方式运行测试。单个文件中的测试按顺序在同一个工作进程中运行。如果您在一个文件中包含许多独立的测试,您可能希望并行运行它们。
import { test } from '@playwright/test';
test.describe.configure({ mode: 'parallel' });
test('runs in parallel 1', async ({ page }) => { /* ... */ });
test('runs in parallel 2', async ({ page }) => { /* ... */ });
Playwright 可以分片 测试套件,以便可以在多台机器上执行。
npx playwright test --shard=1/3
生产力技巧
使用软断言
如果您的测试失败,Playwright 将会显示一条错误消息,说明测试的哪个部分失败了,您可以在 VS Code、终端、HTML 报告或跟踪查看器中看到它。但是,您也可以使用软断言。这些断言不会立即终止测试执行,而是在测试结束后编译并显示一个失败断言列表。
// Make a few checks that will not stop the test when failed...
await expect.soft(page.getByTestId('status')).toHaveText('Success');
// ... and continue the test to check more things.
await page.getByRole('link', { name: 'next page' }).click();