单元测试框架 Playwright 使用入门
2026/4/13 22:34:06 网站建设 项目流程

playwright 介绍

Playwright 是一个端到端(E2E)测试框架, 它可在所有现代浏览器中运行功能强大的测试和自动化。支持多种编程语言 API, 包括 JavaScript 、 TypeScript, Python, .NET 和 Java。正因为它基于浏览器,相当于模拟用户真实操作,因此不光能够用来跑测试用例,还可以用来写爬虫。

Playwright Test for VSCode

我们可以安装一个 vscode 插件 Playwright Test for VSCode,来帮助我们运行、录制、调试测试用例。

初始化项目

如果项目中没有安装PlaywrightNPM 包,或者重新开始一个新的测试项目,需要可以在 vscode 命令面板中输入intsll Playwright

选择我们常用的浏览器,不必担心选错,后面可以在项目中更改。还可以选择 GitHub Action ,这样就可以轻松在 Github 中持续集成。

这里我选择 chromium,这样可以只下载一个浏览器内核。

点击OK后,插件会帮我们自动初始化程序, 下图是初始化的目录结构

配置文件都在playwright.config.ts中。

看下package.json,只包含了一个包@playwright/test

运行测试

所有的测试用例都要写在tests文件夹中,默认有一个测试文件,包含有 2 个测试用例,代码在example.spec.ts中。

第一个测试用例:确保标题包含 Playwright;

第二个测试用例:确保点击 “Get Started”后,跳转到 intro 的链接。

选择左侧的测试用例,并且勾选Show browser,我们便可以直观的看到 Playwright 运行测试的过程。

以上例子默认是使用 chromium 来运行的,并且 chromium 不包含任何 cookie 和缓存信息。

playwright.config.ts配置文件中, 可以配置启用的浏览器为 chrome,我们只需要增加一个参数channel,让 Playwright 使用浏览器来运行。 也可以是其他浏览器,参数可以为: "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge", "msedge-beta", "msedge-dev","msedge-canary".

  1. use: {

  2. + channel:'chrome',

  3. /* Base URL to use in actions like `await page.goto('/')`. */

  4. // baseURL: 'http://127.0.0.1:3000',

  5. /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */

  6. trace: 'on-first-retry',

  7. },

我们虽然改成了使用浏览器来运行,但是启动的浏览器也是一个无痕模式,不包含任何缓存信息。

添加 cookie

测试的系统往往需要登录,而在运行每个测试用例之前运行,都需要登录,这肯定是繁琐的,因此我们可以在运行测试用例之前,手动拷贝 cookies,注入到浏览器中。

比如掘金的每日签到和抽奖,我就可以使用 Playwright 来实现自动化

首先建立一个测试文件

  1. import { test, expect, type Page } from "@playwright/test";

  2. test("登录", async ({page, context}) => {

  3. await context.addCookies([

  4. {

  5. name: "sessionid",

  6. value: "xxx",

  7. path: "/",

  8. domain: ".juejin.cn",

  9. },

  10. {

  11. name: "sessionid_ss",

  12. value: "xxx",

  13. path: "/",

  14. domain: ".juejin.cn",

  15. },

  16. ]);

  17. await page.goto("https://juejin.cn/");

  18. });

打开 chrome 控制台,复制 cookies, 添加到代码中

此时点击左侧运行的测试用例,发现已经是登录状态。

录制一个测试用例

如果要手动去查找 dom ,从零开始写一个测试用例肯定是繁琐的,因此 Playwright VSCode 插件提供了录制功能。

运行上一次测试用例后,浏览器是未关闭的。此时我们点击 vscode 左侧的Record new按钮,vscode 便会自动创建一个测试文件,并且记录操作步骤。

录制时,浏览器又是一个全新的,不保留任何状态,那如果我们要测试的是登录后的功能,岂不是又要登录? 其实 playwright 可以保存登录状态。

在上面测试用例后加一句 storageState。

  1. import { test, expect, type Page } from "@playwright/test";

  2. test("登录", async ({page, context}) => {

  3. await context.addCookies([

  4. {

  5. name: "sessionid",

  6. value: "xxx",

  7. path: "/",

  8. domain: ".juejin.cn",

  9. },

  10. {

  11. name: "sessionid_ss",

  12. value: "xxx",

  13. path: "/",

  14. domain: ".juejin.cn",

  15. },

  16. ]);

  17. await page.goto("https://juejin.cn/");

  18. + await context.storageState({ path: 'state.json' });

  19. });

并且在playwright.config.ts中,配置存储位置。

此时我们录制操作,就已经是登录状态了。

,时长00:55

以下便是录制后的代码。

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

  2. test('test', async ({ page }) => {

  3. await page.goto('https://juejin.cn/');

  4. await page.getByRole('button', { name: '去签到' }).click();

  5. await page.getByRole('button', { name: '立即签到' }).click();

  6. await page.getByRole('button', { name: '去抽奖' }).click();

  7. await page.getByText('免费抽奖次数:1次').click();

  8. await page.getByRole('button', { name: '收下奖励' }).click();

  9. });

录制完成后,直接运行代码可能会报错,我们需要调整一下,因为有些文本是异步请求实现的,有些事件是请求成功后绑定的,在手动录制时,因为已经响应完成,因此没问题,我们加上 2 句延迟。

  1. test("test", async ({ page }) => {

  2. await page.goto("https://juejin.cn/");

  3. + await page.waitForTimeout(1000);

  4. await page.getByRole("button", { name: /去签到|已签到/ }).click();

  5. + await page.waitForTimeout(1000);

  6. await page.getByRole("button", { name: /今日已签到|立即签到/ }).click();

  7. await page.getByRole("button", { name: "去抽奖" }).click();

  8. const lotteryElement = await page.$("#turntable-item-0");

  9. const buttonText = await lotteryElement?.textContent();

  10. if (buttonText === "免费抽奖次数:1次") {

  11. await lotteryElement?.click();

  12. await page.getByRole("button", { name: "收下奖励" }).click();

  13. } else {

  14. expect(

  15. page.locator("#turntable-item-0", { hasText: /单抽/ })

  16. ).toBeDefined();

  17. }

  18. });

便可以运行成功,注意这里我使用了waitForTimeout这个 api 在官网中已经被标记了废弃(deprecate)

实际测试场景中请使用改用网络事件、选择器变得可见等信号。

  1. await page.goto("https://juejin.cn/");

  2. await page.waitForResponse((res) =>

  3. res.url().includes("/user_api/v1/incentive_activity/award_after_login")

  4. );

  5. await page.getByRole("button", { name: /去签到|已签到/ }).click();

  6. await page.waitForResponse((res) =>

  7. res.url().includes("/growth_api/v2/get_today_status")

  8. );

  9. await page.getByRole("button", { name: /今日已签到|立即签到/ }).click();

等待接口响应成功后再出发点击事件。

还有一点就是,自动录制的代码,一般使用了语义化定位方法,比如getByRolegetByText,这些定位器往往不够准确,改动代码会导致测试用例失效。

因此我们可以使用locator定位器来替换。

在 Playwright 中,Locator 表示一种元素查找方式,是 Playwright 提供的一组方法,用于定位页面上的元素。

Locator 支持 XPath 和 CSS 选择器

  1. await page.locator(

  2. '#tsf > div:nth-child(2) > div.A8SBwf > div.RNNXgb > div > div.a4bIc > input'

  3. ).click();

  4. await page

  5. .locator('//*[@id="tsf"]/div[2]/div[1]/div[1]/div/div[2]/input')

  6. .click();

在 vscode 中可以使用 Pick locator 快速活动当前的 dom 定位。

测试用例及断言

录制的测试代码只能确保业务能够跑通,但不能证明程序的可靠与健壮。一旦测试用例出错,也不知道是程序错误还是测试用例错误,因此我们还是需要根据测试用例来写可靠的测试代码。

比如上述掘金抽奖程序可以包含以下测试用例

  1. 签到的状态需要根据接口返回显示

通过 network 查看签到返回如下:

因此我的签到测试用例代码如下

  1. test("签到的状态根据接口返回显示", async ({ page }) => {

  2. await page.goto("https://juejin.cn/user/center/signin");

  3. const promise = await page.waitForResponse((res) =>

  4. res.url().includes("/growth_api/v2/get_today_status")

  5. );

  6. const res = await promise.json();

  7. if (res.data.check_in_done) {

  8. await expect(page.locator(".signedin")).toHaveText("今日已签到");

  9. } else {

  10. await expect(page.locator(".signedin")).toHaveText("立即签到");

  11. await page.getByRole("button", { name: /立即签到/ }).click();

  12. await page.getByRole("button", { name: "去抽奖" }).click();

  13. //调整到抽奖页面

  14. await expect(page).toHaveURL(/user\/center\/lottery/);

  15. }

  16. });

  17. 抽奖页面,根据接口返回显示抽奖次数和奖品

通过 network,看到抽奖配置接口返回如下:

因此我的测试用例代码如下

  1. test("根据接口返回显示抽奖次数", async ({ page }) => {

  2. await page.goto("https://juejin.cn/user/center/lottery");

  3. const promise = await page.waitForResponse((res) =>

  4. res.url().includes("/growth_api/v1/lottery_config/get")

  5. );

  6. const res = await promise.json();

  7. const lotteryNames = res.data.lottery.map((item) => {

  8. if (item.unlock_count === 0) {

  9. return new RegExp(item.lottery_name);

  10. } else {

  11. return new RegExp(`再抽${item.unlock_count}次解锁`);

  12. }

  13. });

  14. await expect(page.locator(".item-container .turntable-item")).toHaveText(

  15. lotteryNames

  16. );

  17. if (res.data.free_count) {

  18. await expect(page.locator("#turntable-item-0")).toHaveText(

  19. `免费抽奖次数:${res.data.free_count}次`

  20. );

  21. } else {

  22. await expect(page.locator("#turntable-item-0")).toHaveText("单抽 200");

  23. }

  24. });

有了以上断言,我们便可以确保前端页面显示与接口返回显示一致。

运行完成后,可以在 playwright-report 查看测试报告。

当然这是我学习 e2e 测试的一个例子,不够准确,也不够详情。

小结

本文介绍了 Playwright 测试框架的入门使用,Playwright 是一个功能强大的端到端(E2E)测试框架,支持多种编程语言 API,适用于现代浏览器,还可用于编写网络爬虫。

首先介绍了 Playwright Test for VSCode 插件,以及如何初始化测试项目,如何运行测试用例,并指出可以选择不同的浏览器作为测试环境, 如何添加 Cookie 来模拟登录状态,以及如何使用录制功能来自动生成测试代码。

另外,文章强调了使用 Locator 定位器替代语义化定位方法,以提高测试的准确性。最后,我们通过了一个掘金抽奖程序实例强调了断言的重要性,以确保测试代码的可靠性。

感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!有需要的小伙伴可以点击下方小卡片领取

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

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

立即咨询