PlayWright教程

4 阅读7分钟

Playwright 教程

1. Playwright 是什么

Playwright 是一个前端自动化测试工具,可以用来:

  • 做 Web 自动化测试
  • 模拟用户点击、输入、跳转
  • 跑 Chrome、Edge、Firefox、Safari(WebKit)
  • 截图、录视频、抓日志
  • 做接口拦截、Mock 数据
  • 跑 E2E 测试、UI 测试、回归测试

它的优点是:

  • 跨浏览器
  • API 比较现代
  • 自动等待机制很好用
  • 对单页应用支持不错
  • 官方测试框架比较完整

2. 适合谁学

适合这些场景:

  • 前端开发想补测试能力
  • QA/测试工程师做自动化
  • 全栈项目做端到端测试
  • 想做网页爬取/自动操作(合规前提下)

3. 安装

3.1 创建项目

mkdir playwright-demo
cd playwright-demo
npm init -y

3.2 安装 Playwright

推荐直接装测试框架版本:

npm init playwright@latest

安装时通常会让你选择:

  • JavaScript / TypeScript
  • 测试目录
  • 是否生成 GitHub Actions
  • 是否安装浏览器

如果你已经有项目,也可以手动安装:

npm install -D @playwright/test
npx playwright install

4. 项目结构

初始化后一般像这样:

playwright-demo/
  tests/
    example.spec.ts
  playwright.config.ts
  package.json

常见文件说明:

  • tests/:测试文件目录
  • playwright.config.ts:全局配置
  • package.json:脚本和依赖

5. 第一个测试

新建文件 tests/demo.spec.ts

import { test, expect } from '@playwright/test';

test('首页标题测试', async ({ page }) => {
  await page.goto('https://playwright.dev/');
  await expect(page).toHaveTitle(/Playwright/);
});

运行测试:

npx playwright test

只跑某个文件:

npx playwright test tests/demo.spec.ts

打开可视化 UI:

npx playwright test --ui

6. 基本概念

6.1 test

定义一个测试用例:

test('测试名称', async ({ page }) => {
  // 测试逻辑
});

6.2 page

浏览器中的一个页面标签,相当于一个 tab。

6.3 expect

断言工具,用来验证结果是否符合预期。


7. 常用页面操作

7.1 打开页面

await page.goto('https://example.com');

7.2 点击元素

await page.locator('button').click();

7.3 输入内容

await page.locator('#username').fill('admin');
await page.locator('#password').fill('123456');

7.4 按键操作

await page.locator('#search').press('Enter');

7.5 获取文本

const text = await page.locator('.title').textContent();

7.6 选择下拉框

await page.locator('select').selectOption('option1');

7.7 勾选复选框

await page.locator('#agree').check();

8. Locator 定位器

Playwright 推荐优先用 语义化定位,而不是一堆脆弱的 CSS 路径。

推荐写法

page.getByRole('button', { name: '登录' })
page.getByLabel('用户名')
page.getByPlaceholder('请输入手机号')
page.getByText('提交')
page.getByTestId('submit-btn')

示例

await page.getByLabel('用户名').fill('zhangsan');
await page.getByLabel('密码').fill('123456');
await page.getByRole('button', { name: '登录' }).click();

不太推荐但也常用

page.locator('.login-btn')
page.locator('#username')
page.locator('//button[text()="登录"]')

最佳实践

优先顺序建议:

  1. getByRole
  2. getByLabel
  3. getByPlaceholder
  4. getByTestId
  5. locator(css/xpath)

9. 断言

9.1 标题断言

await expect(page).toHaveTitle(/Playwright/);

9.2 URL 断言

await expect(page).toHaveURL(/dashboard/);

9.3 文本断言

await expect(page.getByText('登录成功')).toBeVisible();

9.4 输入框值断言

await expect(page.locator('#username')).toHaveValue('admin');

9.5 元素显示/隐藏

await expect(page.locator('.loading')).toBeHidden();
await expect(page.locator('.success')).toBeVisible();

10. 自动等待

Playwright 一个很好用的点是自动等待元素可操作

比如:

await page.getByRole('button', { name: '提交' }).click();

它会自动等按钮出现、可见、可点击。

所以一般不要滥用 waitForTimeout

await page.waitForTimeout(5000); // 不推荐

推荐等待明确条件:

await expect(page.getByText('提交成功')).toBeVisible();

或者:

await page.locator('.result').waitFor();

11. 登录测试示例

import { test, expect } from '@playwright/test';

test('用户登录', async ({ page }) => {
  await page.goto('https://example.com/login');

  await page.getByLabel('用户名').fill('testuser');
  await page.getByLabel('密码').fill('123456');
  await page.getByRole('button', { name: '登录' }).click();

  await expect(page).toHaveURL(/dashboard/);
  await expect(page.getByText('欢迎你')).toBeVisible();
});

12. 录制脚本

Playwright 自带代码生成器,特别适合入门。

npx playwright codegen https://example.com

你在浏览器里操作,它会自动生成脚本。

适合:

  • 快速熟悉 API
  • 快速拿定位器
  • 生成初版测试代码

但生成的代码通常要手工整理,别直接长期拿来维护。


13. 调试方法

13.1 暂停调试

await page.pause();

运行时:

npx playwright test --debug

13.2 headed 模式

默认是无头浏览器,可改成有界面:

npx playwright test --headed

13.3 查看 Trace

Playwright 的 trace 很强,失败后可以看完整过程。

运行后查看:

npx playwright show-trace trace.zip

14. 截图和视频

截图

await page.screenshot({ path: 'screenshot.png', fullPage: true });

配置失败时保留截图/视频

playwright.config.ts

use: {
  screenshot: 'only-on-failure',
  video: 'retain-on-failure',
  trace: 'on-first-retry',
}

15. 多浏览器执行

Playwright 可以同时跑 Chromium / Firefox / WebKit。

配置文件示例:

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

export default defineConfig({
  testDir: './tests',
  use: {
    headless: true,
  },
  projects: [
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
    },
    {
      name: 'firefox',
      use: { ...devices['Desktop Firefox'] },
    },
    {
      name: 'webkit',
      use: { ...devices['Desktop Safari'] },
    },
  ],
});

运行全部浏览器:

npx playwright test

只跑某一个:

npx playwright test --project=chromium

16. Hooks 钩子

每个测试前执行

test.beforeEach(async ({ page }) => {
  await page.goto('https://example.com/login');
});

全部测试前执行

test.beforeAll(async () => {
  // 初始化数据
});

17. 复用登录状态

如果每条测试都重新登录,会很慢。 可以把登录状态存起来。

保存登录态

import { test as setup } from '@playwright/test';

setup('authenticate', async ({ page }) => {
  await page.goto('https://example.com/login');
  await page.getByLabel('用户名').fill('admin');
  await page.getByLabel('密码').fill('123456');
  await page.getByRole('button', { name: '登录' }).click();

  await page.context().storageState({ path: 'playwright/.auth/user.json' });
});

使用登录态

use: {
  storageState: 'playwright/.auth/user.json',
}

这在真实项目里很常见。


18. 接口监听与 Mock

18.1 监听响应

const response = await page.waitForResponse(resp =>
  resp.url().includes('/api/user') && resp.status() === 200
);

18.2 Mock 接口

await page.route('**/api/user', async route => {
  await route.fulfill({
    status: 200,
    contentType: 'application/json',
    body: JSON.stringify({
      name: '老张',
      age: 18
    }),
  });
});

适合:

  • 前后端并行开发
  • 测试异常状态
  • 测试边界场景

19. 文件上传和下载

上传文件

await page.locator('input[type="file"]').setInputFiles('./test-data/a.png');

下载文件

const downloadPromise = page.waitForEvent('download');
await page.getByText('下载').click();
const download = await downloadPromise;
await download.saveAs('./downloads/report.xlsx');

20. iframe 处理

const frame = page.frameLocator('#my-frame');
await frame.getByRole('button', { name: '提交' }).click();

21. 新窗口处理

const [newPage] = await Promise.all([
  page.waitForEvent('popup'),
  page.getByText('打开新窗口').click(),
]);

await expect(newPage).toHaveURL(/new-page/);

22. 页面对象模型 POM

项目变大后,不要把定位器和操作全堆在测试里。 可以封装成页面对象。

pages/LoginPage.ts

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

export class LoginPage {
  constructor(private page: Page) {}

  async goto() {
    await this.page.goto('https://example.com/login');
  }

  async login(username: string, password: string) {
    await this.page.getByLabel('用户名').fill(username);
    await this.page.getByLabel('密码').fill(password);
    await this.page.getByRole('button', { name: '登录' }).click();
  }
}

测试里使用

import { test, expect } from '@playwright/test';
import { LoginPage } from '../pages/LoginPage';

test('POM 登录测试', async ({ page }) => {
  const loginPage = new LoginPage(page);

  await loginPage.goto();
  await loginPage.login('admin', '123456');

  await expect(page).toHaveURL(/dashboard/);
});

23. 常用命令总结

# 初始化
npm init playwright@latest

# 安装浏览器
npx playwright install

# 运行测试
npx playwright test

# 跑某个文件
npx playwright test tests/demo.spec.ts

# 只跑某个浏览器
npx playwright test --project=chromium

# 有界面运行
npx playwright test --headed

# 调试模式
npx playwright test --debug

# 可视化 UI
npx playwright test --ui

# 录制脚本
npx playwright codegen https://example.com

24. 学习路线建议

建议按这个顺序学:

第一步:先会跑起来

  • 安装 Playwright
  • 跑官方示例
  • 能写一个最简单测试

第二步:掌握基础操作

  • goto
  • click
  • fill
  • expect
  • Locator

第三步:掌握真实项目能力

  • 登录流程
  • 等待机制
  • 截图 / Trace / Debug
  • 多浏览器运行

第四步:进阶

  • POM 封装
  • 登录态复用
  • 接口 Mock
  • CI 集成

25. 新手最常见的坑

坑 1:乱用 CSS/XPath

定位太脆弱,页面一改就挂。

建议: 优先用 getByRolegetByLabelgetByTestId

坑 2:强行 sleep

await page.waitForTimeout(3000);

这样很不稳定。

建议: 等元素、等 URL、等响应、等断言成立。

坑 3:测试之间互相依赖

上一条测试创建的数据,下一条测试又依赖它,容易连锁失败。

建议: 测试尽量独立。

坑 4:一个测试写太长

一个 case 里塞十几个流程,不好维护。

建议: 一个测试验证一个核心目标。


26. 一份实战模板

import { test, expect } from '@playwright/test';

test.describe('用户中心', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('https://example.com/login');
    await page.getByLabel('用户名').fill('admin');
    await page.getByLabel('密码').fill('123456');
    await page.getByRole('button', { name: '登录' }).click();
    await expect(page).toHaveURL(/dashboard/);
  });

  test('查看个人资料', async ({ page }) => {
    await page.getByText('个人中心').click();
    await expect(page.getByText('基本信息')).toBeVisible();
  });

  test('修改昵称', async ({ page }) => {
    await page.getByText('个人中心').click();
    await page.getByLabel('昵称').fill('新昵称');
    await page.getByRole('button', { name: '保存' }).click();
    await expect(page.getByText('保存成功')).toBeVisible();
  });
});

27. 一句话理解 Playwright

你可以把 Playwright 理解成:

“用代码操控浏览器,像真人一样操作页面,并验证结果对不对。”


28. 推荐资料