零基础入门Playwright:跨浏览器自动化与端到端测试实战指南
1. 项目概述:为什么是Playwright?
如果你正在寻找一个能让你从零开始,快速上手浏览器自动化和端到端测试的工具,那么现在上车Playwright,绝对是一个不会后悔的决定。我接触过不少自动化工具,从早期的Selenium,到后来的Puppeteer,再到现在的Playwright,可以说Playwright是目前综合体验最好的一个。它不仅仅是一个测试框架,更是一个强大的浏览器自动化库,由微软团队开发并维护,背靠大树,生态和更新速度都很有保障。
简单来说,Playwright能让你用一套代码,同时驱动Chromium(Chrome/Edge内核)、Firefox和WebKit(Safari内核)三大浏览器引擎。这意味着你写的自动化脚本或测试用例,可以无缝地在几乎所有现代浏览器上运行,彻底告别了“在我机器上好好的”这种尴尬。无论是想写个脚本自动填表、批量截图、爬取动态网页数据,还是为你的Web应用构建一套健壮的自动化测试体系,Playwright都能胜任。
对于零基础的朋友,最怕的就是环境配置复杂、API难懂、动不动就报一些看不懂的错误。Playwright在设计之初就考虑到了这些问题,它的安装过程极其简单,API设计非常现代和人性化,并且提供了“自动等待”、“智能定位器”等特性,让你能把精力更多地放在业务逻辑上,而不是和元素加载时机、异步操作这些底层细节搏斗。接下来,我就带你从最基础的安装开始,一步步拆解Playwright的核心能力,并分享一些我踩过坑后才总结出来的实战技巧。
2. 环境准备与核心概念扫盲
2.1 一站式安装与初始化
Playwright的安装是我见过最省心的之一。官方推荐使用npm init playwright@latest这个命令,它能帮你完成所有事情:创建一个新的项目(或集成到现有项目)、安装Playwright Test测试运行器、以及下载所需的浏览器二进制文件。
打开你的终端(命令行),在你希望创建项目的目录下,执行:
npm init playwright@latest执行后,你会看到一个交互式的命令行界面。它会问你几个问题:
- Where to put your end-to-end tests?(测试文件存放路径,默认是
tests或e2e) - Add a GitHub Actions workflow?(是否添加GitHub Actions工作流,用于CI/CD,初学者可以先选否)
- Install Playwright browsers?(是否安装浏览器,一定要选是!)
- Install Playwright operating system dependencies?(是否安装系统依赖,如Linux上的字体库等,建议选是)
一路回车用默认选项,或者根据你的需要选择。完成后,你的项目目录里会多出几个关键文件:
playwright.config.ts: Playwright的配置文件,可以设置浏览器类型、运行模式、超时时间、截图视频存储路径等。tests/目录: 存放你的测试文件。tests-examples/目录: 官方提供的示例测试文件,非常适合学习。package.json: 可以看到已经添加了@playwright/test依赖。
注意:安装浏览器这一步可能会耗时几分钟,因为它需要下载Chromium、Firefox和WebKit三大浏览器的完整二进制文件到本地(通常在
node_modules/playwright-core/.local-browsers目录下)。请确保网络通畅。这也是Playwright的优势之一——浏览器环境是自包含的,与系统安装的浏览器无关,保证了环境的一致性。
如果你只是想快速写个脚本玩玩,而不是做完整的测试项目,也可以单独安装Playwright库:
npm install playwright这样安装的playwright包,主要提供底层的浏览器自动化API,不包含测试运行器。我们后续的讲解会以@playwright/test这个测试运行器为主,因为它功能更全,也更适合构建可维护的自动化项目。
2.2 理解Playwright的核心组件
在开始写代码前,理解Playwright的几个核心概念和组件关系,能让你后续的学习事半功倍。很多人上来就写page.goto(),但不知道为什么有时会报错,其实是对它的架构理解不深。
1. Playwright Test vs Playwright Library这是最容易混淆的点。你可以把它们理解为“套装”和“基础工具包”。
- Playwright Library (
playwright): 是核心的浏览器自动化驱动库。它提供了启动浏览器、创建页面、操作元素的所有底层API。如果你只需要写一个简单的爬虫或自动化脚本,用这个就够了。 - Playwright Test (
@playwright/test): 是一个构建在Library之上的、功能完整的测试运行器。它除了包含Library的所有能力,还额外提供了测试结构(test)、断言(expect)、夹具(Fixtures,如page)、并行执行、报告生成、追踪(Trace)等专为测试设计的强大功能。对于做自动化测试,强烈推荐直接使用它。
2. 架构层级:Browser -> BrowserContext -> Page这是Playwright操作浏览器的核心模型,像俄罗斯套娃。
- Browser: 对应一个浏览器实例。通过
await chromium.launch()启动。你可以设置无头模式(headless: true,不显示界面)或是有头模式(headless: false,显示界面方便调试)。 - BrowserContext: 浏览器上下文。这是Playwright中非常重要的一个概念,它相当于一个独立的“隐身会话”。每个Context拥有独立的cookie、localStorage、会话历史,彼此完全隔离。一个Browser可以创建多个Context。这种隔离性为测试的并行化和独立性提供了基础。
- Page: 标签页。一个Context可以拥有多个Page。我们绝大部分的页面操作,如导航、点击、输入,都是在Page对象上进行的。
这种层级关系带来了巨大的灵活性。例如,你可以用一个Browser,创建两个Context来模拟两个完全独立的用户会话进行测试;也可以在一个Context里打开多个Page来操作多个标签页。
3. 自动等待(Auto-waiting)这是Playwright相比Selenium等老工具最革命性的改进之一。在Playwright中,几乎所有的操作(如click,fill,check)都内置了智能等待。它不会盲目地等待一个固定时间,而是会等待目标元素经历一系列可操作性检查:
- 元素是否附加(Attached)到DOM?
- 元素是否可见(Visible)?
- 元素是否启用(Enabled)?
- 元素是否稳定(稳定指元素没有动画或布局变化)? 只有所有这些条件都满足,操作才会执行。这极大地减少了测试中因元素未加载完成而导致的“脆性失败”,让你几乎不再需要写
sleep或显式的waitForSelector。
3. 从零编写你的第一个自动化脚本
理论说再多,不如动手写一行代码。让我们从一个最简单的例子开始,感受一下Playwright的流畅。
3.1 基础导航与截图
在你的项目tests目录下,新建一个文件,比如叫example.spec.ts(.spec.ts是Playwright Test默认识别的测试文件后缀)。输入以下内容:
import { test, expect } from '@playwright/test'; test('访问Playwright官网并检查标题', async ({ page }) => { // 1. 导航到页面 await page.goto('https://playwright.dev/'); // 2. 断言页面标题包含“Playwright” await expect(page).toHaveTitle(/Playwright/); // 3. 对页面进行截图 await page.screenshot({ path: 'screenshot-homepage.png' }); });保存文件,然后在终端运行这个测试:
npx playwright test example.spec.ts你会看到测试启动,在无头模式下运行,并输出结果。如果一切正常,项目根目录下会生成一张名为screenshot-homepage.png的截图。
代码拆解:
import { test, expect }: 从@playwright/test导入测试函数和断言库。test('描述', async ({ page }) => { ... }): 定义一个测试用例。{ page }是一个测试夹具(Fixture),Playwright Test会自动为你创建并管理一个干净的Page对象,无需手动browser.newPage()。page.goto(): 导航到指定URL。expect(page).toHaveTitle(): 使用Playwright增强的断言,它会自动重试直到条件满足或超时。page.screenshot(): 截取当前页面视图。
实操心得: 默认情况下,
npx playwright test会以无头模式运行,并同时使用Chromium, Firefox, WebKit三个浏览器运行测试(在playwright.config.ts中配置)。如果你只想用Chromium快速跑一下,可以加上参数:npx playwright test example.spec.ts --project=chromium。如果想看到浏览器界面进行调试,加上--headed参数。
3.2 元素定位与交互:告别XPath噩梦
定位页面元素是自动化的基石。Playwright提供了一套非常强大且人性化的定位器(Locator)API,其设计哲学是“像用户一样定位”。
1. 最佳实践定位器(优先级从高到低):
getByRole():首推!通过ARIA角色定位,如button,link,textbox。这是最接近用户感知的方式,因为用户看到的就是一个“按钮”或“链接”。通常需要结合name选项(即可访问性名称)来精确定位。await page.getByRole('button', { name: 'Submit' }).click(); await page.getByRole('link', { name: 'Get started' }).click();getByText()和getByLabel(): 通过可见文本或关联的label文本定位。也非常稳定。await page.getByText('Welcome back').click(); await page.getByLabel('User Name').fill('myusername');getByPlaceholder(): 通过输入框的占位符定位。getByTestId(): 通过开发者专门为测试添加的数据属性定位(如>// 在HTML中:<button>import { test, expect } from '@playwright/test'; test('用户登录成功', async ({ page }) => { // 导航到登录页 await page.goto('https://your-app.com/login'); // 使用最佳实践定位器填写表单 await page.getByLabel('Email Address').fill('user@example.com'); await page.getByLabel('Password').fill('securepassword123'); // 点击登录按钮 await page.getByRole('button', { name: 'Sign In' }).click(); // 断言登录成功:页面跳转,且出现用户欢迎信息 await expect(page).toHaveURL('https://your-app.com/dashboard'); await expect(page.getByText('Welcome, user@example.com')).toBeVisible(); });3. 处理复杂场景:等待与断言Playwright的断言是“Web优先”且“自动重试”的。这意味着
expect(locator).toBeVisible()会不断检查元素状态,直到它可见或超时。你很少需要手动写waitForSelector。// 等待一个元素出现并可见 await expect(page.getByText('Loading finished')).toBeVisible(); // 等待一个元素从DOM中消失 await expect(page.getByText('Loading...')).toBeHidden(); // 或者 .not.toBeVisible() // 断言元素数量 await expect(page.getByRole('listitem')).toHaveCount(10); // 断言输入框的值 await expect(page.getByLabel('Search')).toHaveValue('playwright');避坑技巧: 有时元素被其他元素遮挡(例如一个模态框),即使它存在于DOM且可见,点击也会失败。Playwright默认会检查元素是否可操作(actionable)。如果遇到点击问题,可以尝试
force: true选项(await locator.click({ force: true }))来绕过可操作性检查,但请谨慎使用,因为这可能模拟出用户无法实现的交互。4. 进阶功能与实战配置详解
掌握了基本操作后,我们来探索Playwright那些让自动化变得轻松愉快的进阶特性。
4.1 测试隔离与认证状态复用
这是Playwright Test框架层的核心优势。每个测试用例都运行在独立的
BrowserContext中,天然隔离。但登录操作往往很耗时,我们希望在每个测试前自动登录。解决方案:使用
storageState和项目配置。首先,创建一个设置文件(setup)来登录并保存状态: 新建
tests/auth.setup.ts:import { test as setup, expect } from '@playwright/test'; // 这个“test”被重命名为“setup”,以区别于普通测试用例 setup('authenticate', async ({ page }) => { await page.goto('https://your-app.com/login'); await page.getByLabel('Email').fill('test@example.com'); await page.getByLabel('Password').fill('password'); await page.getByRole('button', { name: 'Sign In' }).click(); // 等待登录成功的标志出现 await expect(page.getByText('My Dashboard')).toBeVisible(); // 将当前上下文的存储状态(cookies, localStorage)保存到文件 await page.context().storageState({ path: 'playwright/.auth/user.json' }); });运行这个设置:
npx playwright test auth.setup.ts --project=chromium。运行成功后,会在playwright/.auth/目录下生成user.json文件。然后,在
playwright.config.ts中配置全局使用这个状态:import { defineConfig } from '@playwright/test'; export default defineConfig({ // ... 其他配置 ... use: { // 所有测试的默认配置 storageState: 'playwright/.auth/user.json', // 指向保存的状态文件 }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, { name: 'setup', // 一个专门用于运行setup的项目 testMatch: /.*\.setup\.ts/, // 匹配所有setup文件 }, { name: 'chromium logged in', use: { ...devices['Desktop Chrome'] }, dependencies: ['setup'], // 声明依赖,确保setup先运行 }, // 可以配置更多项目,如 firefox, webkit ], });最后,你的普通测试用例就可以直接访问已登录的页面了:
test('访问需要登录的个人资料页', async ({ page }) => { // 无需再登录,因为storageState已经注入了认证状态 await page.goto('https://your-app.com/profile'); // 直接断言登录后的内容 await expect(page.getByText('Welcome, test@example.com')).toBeVisible(); });运行测试时,使用
npx playwright test --project="chromium logged in"。
4.2 网络请求拦截与模拟(Mocking)
自动化测试中,我们经常需要控制网络行为,比如屏蔽图片加速测试、模拟API返回、测试错误场景等。Playwright的
page.route()功能非常强大。示例1:拦截并修改API响应
import { test, expect } from '@playwright/test'; test('模拟API返回空列表', async ({ page }) => { // 在导航前,先拦截特定的API请求 await page.route('**/api/todos', async route => { // 构造一个模拟的JSON响应 const mockResponse = { todos: [] }; // 使用模拟响应来继续请求 await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify(mockResponse), }); }); await page.goto('https://your-app.com/todos'); // 页面应该显示“暂无待办事项” await expect(page.getByText('No todos yet')).toBeVisible(); });示例2:中止不必要的资源加载(如图片、样式)以加速测试
await page.route('**/*.{png,jpg,jpeg,svg,gif,css}', route => route.abort()); await page.goto('https://example.com'); // 这个页面加载会快很多4.3 录制与代码生成:Playwright CodeGen
对于初学者或者快速探索一个页面,手动写定位器可能比较慢。Playwright内置了录制功能(CodeGen),可以让你通过操作浏览器自动生成代码。
- 打开录制器:
npx playwright codegen https://your-app.com - 这会打开两个窗口:一个浏览器,一个代码生成器。你在浏览器里的所有点击、输入、导航操作,都会实时转换成Playwright代码,显示在代码生成器窗口中。
- 操作完成后,你可以直接复制生成的代码到你的测试文件中。
注意事项: 生成的代码是一个很好的起点,但通常需要优化。它可能过度依赖CSS选择器或XPath,你应该手动将其替换为更稳定的
getByRole、getByText等定位器。CodeGen更适合用于理解页面交互流和快速生成代码骨架。4.4 追踪(Tracing)与调试:问题排查利器
测试失败了,为什么?元素没找到?网络请求错了?Playwright的追踪(Trace)功能可以记录测试执行的完整过程,像一台时光机。
1. 启用追踪在
playwright.config.ts中配置:export default defineConfig({ use: { trace: 'on-first-retry', // 仅在第一次重试时记录(节省资源)。也可用 'on'(始终记录)或 'retain-on-failure'(仅在失败时保留) }, });2. 查看追踪当测试失败(或在配置的条件下),会在
test-results/目录下生成一个trace.zip文件。用以下命令打开可视化查看器:npx playwright show-trace test-results/你的测试结果目录/trace.zipTrace Viewer会展示一个时间轴,包含每一步操作的截图、DOM快照、网络请求、控制台日志。你可以一步步回放测试,精确看到在哪一步、页面是什么状态、发生了什么错误。这是调试复杂测试用例的神器。
5. 工程化实践与常见问题排查
当你的自动化脚本或测试套件逐渐庞大,就需要考虑工程化的问题了。
5.1 配置文件(playwright.config.ts)深度解析
一个合理的配置是高效运行测试的基础。我们来详解关键配置项:
import { defineConfig, devices } from '@playwright/test'; export default defineConfig({ // 测试文件的位置 testDir: './tests', // 匹配哪些文件是测试文件 testMatch: '**/*.spec.ts', // 忽略哪些文件 testIgnore: '**/node_modules/**', // 全局超时设置(毫秒) timeout: 30 * 1000, // 每个测试用例最多30秒 // 每个断言(expect)的超时 expect: { timeout: 5000, // 5秒 }, // 是否并行运行测试 fullyParallel: true, // 每个工作进程(worker)的失败测试重试次数 retries: process.env.CI ? 2 : 0, // 在CI环境中重试2次,本地不重试 // 工作进程数量,通常设为CPU核心数或 `'50%'` workers: process.env.CI ? 1 : '50%', // CI环境串行保证稳定,本地用一半CPU核心并行 // 报告生成器 reporter: [ ['html'], // 生成漂亮的HTML报告 ['list'], // 在控制台输出简洁列表 ['junit', { outputFile: 'results.xml' }], // 生成JUnit格式报告,用于CI集成 ], // 所有测试的共享配置 use: { // 基础URL,这样测试中可以用相对路径:`await page.goto('/login');` // baseURL: 'http://localhost:3000', // 自动截屏:仅在测试失败时截取 screenshot: 'only-on-failure', // 自动录屏:仅在测试失败时录制 video: 'retain-on-failure', // 追踪配置 trace: 'on-first-retry', }, // 项目配置:可以定义多套环境,如不同浏览器、不同设备、不同用户状态 projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, { name: 'firefox', use: { ...devices['Desktop Firefox'] }, }, { name: 'webkit', use: { ...devices['Desktop Safari'] }, }, // 模拟移动端 { name: 'Mobile Chrome', use: { ...devices['Pixel 5'] }, }, { name: 'Mobile Safari', use: { ...devices['iPhone 13'] }, }, // 带认证状态的项目(参考4.1节) // { // name: 'chromium auth', // use: { ...devices['Desktop Chrome'], storageState: 'playwright/.auth/user.json' }, // dependencies: ['setup'], // }, ], // Web服务器配置:在运行测试前,自动启动你的本地开发服务器 // webServer: { // command: 'npm run start', // url: 'http://localhost:3000', // reuseExistingServer: !process.env.CI, // timeout: 120 * 1000, // }, });5.2 常见问题与排查技巧实录
即使有了强大的工具,踩坑也是难免的。下面是我在实际项目中遇到的一些典型问题及解决方案。
问题1:定位器找不到元素(
locator.click: Target closed或Timeout)- 可能原因1:元素在iframe或shadow DOM内。
- 解决方案: 先定位到iframe或shadow host,再在其内部查找。
// 处理iframe const frame = page.frame({ name: 'my-iframe' }); await frame.locator('button').click(); // 处理shadow DOM (需要 `locator` 链式调用) await page.locator('my-custom-element').locator('button').click(); - 可能原因2:动态内容加载太慢,超过了默认超时时间。
- 解决方案: 增加超时时间,或检查网络/前端性能。也可以使用
locator.waitFor()。
await page.locator('.dynamic-content').waitFor({ state: 'visible' }); await page.locator('.dynamic-content').click({ timeout: 10000 }); // 给点击操作10秒超时 - 解决方案: 增加超时时间,或检查网络/前端性能。也可以使用
- 可能原因3:页面有多个匹配元素,定位器不够精确。
- 解决方案: 使用更精确的定位器,如
getByRole加上name,或者使用locator.first(),locator.nth(index),或者用locator.filter()进行过滤。
await page.getByRole('button').filter({ hasText: 'Save' }).click(); - 解决方案: 使用更精确的定位器,如
问题2:点击或输入没有效果
- 可能原因1:元素被遮挡(如弹窗、遮罩层)。
- 解决方案: 检查是否有模态框未关闭。可以尝试
force: true,但更好的方法是先关闭遮挡物。
- 解决方案: 检查是否有模态框未关闭。可以尝试
- 可能原因2:元素是
<div>伪装的按钮,没有正确的角色或事件监听。- 解决方案: 可能需要触发的是
page.keyboard.press(‘Enter’)或使用page.evaluate()执行原生JS点击。
await page.locator(‘.div-button’).evaluate(node => node.click()); - 解决方案: 可能需要触发的是
问题3:在CI(如GitHub Actions)环境中运行失败,本地却成功
- 可能原因1:CI环境没有安装浏览器依赖。
- 解决方案: 确保CI脚本中运行了
npx playwright install --with-deps或npx playwright install chromium(如果只用一个浏览器)。--with-deps会安装必要的系统库(如字体)。
- 解决方案: 确保CI脚本中运行了
- 可能原因2:CI环境资源(CPU/内存)不足,或超时时间太短。
- 解决方案: 在CI配置中增加资源,或适当增加
timeout配置。将workers设为1以串行运行,减少资源争抢。
- 解决方案: 在CI配置中增加资源,或适当增加
- 可能原因3:测试依赖本地服务(如
localhost:3000),CI中未启动。- 解决方案: 使用
webServer配置(见上文)在测试前自动启动服务,并确保baseURL配置正确。
- 解决方案: 使用
问题4:如何调试一个具体的测试?不要总用
npx playwright test跑全部。试试这些方法:- 运行单个文件:
npx playwright test your-test.spec.ts - 运行单个测试用例:
npx playwright test -g “测试用例描述” - 使用UI模式(强烈推荐):
npx playwright test --ui。这会打开一个图形化界面,可以查看测试列表、单独运行、查看时间线、并且每一步都有实时浏览器视图和操作日志,是交互式调试的终极工具。 - 使用
--debug标志:npx playwright test --debug。会在第一个测试处暂停,并打开浏览器开发者工具。 - 在VS Code中调试: 安装“Playwright Test for VSCode”扩展,可以直接在IDE里设置断点、单步调试。
问题5:Playwright和Puppeteer、Selenium有什么区别?怎么选?这是一个常见问题。简单对比:
- Selenium: 老牌,支持语言多,生态庞大。但API较老,需要手动处理等待,速度较慢,配置WebDriver较繁琐。
- Puppeteer: 由Chrome团队开发,只支持Chrome/Chromium,API现代,性能好。但跨浏览器能力弱。
- Playwright: 由微软团队开发(部分成员来自Puppeteer团队)。继承了Puppeteer的现代API和性能,同时原生支持Chromium、Firefox、WebKit三大引擎。内置了自动等待、测试运行器、追踪等强大功能,开箱即用体验最好。
选择建议: 对于全新的Web自动化项目,尤其是涉及跨浏览器测试的,无脑选Playwright。如果只需要Chrome且对现有Puppeteer代码有依赖,可以继续用Puppeteer。Selenium则在维护遗留项目或需要特定语言绑定时考虑。
6. 融入现代开发流程:CI/CD与AI赋能
自动化脚本的价值在于持续运行。将其集成到CI/CD(持续集成/持续部署)流水线中,才能实现“质量守门员”的作用。
6.1 集成到GitHub Actions
在项目根目录创建
.github/workflows/playwright.yml:name: Playwright Tests on: push: branches: [ main, develop ] pull_request: branches: [ main ] jobs: test: timeout-minutes: 60 runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 'lts/*' - name: Install dependencies run: npm ci - name: Install Playwright Browsers run: npx playwright install --with-deps chromium # CI中通常只安装一个浏览器以加快速度 - name: Run Playwright tests run: npx playwright test --project=chromium - uses: actions/upload-artifact@v4 if: always() # 无论测试成功失败,都上传报告 with: name: playwright-report path: playwright-report/ retention-days: 30这个工作流会在每次推送到主分支或创建Pull Request时,安装依赖、安装Playwright(仅Chromium)、运行测试,并将生成的HTML报告上传为制品,方便下载查看。
6.2 拥抱AI:Playwright CLI与MCP
从你提供的热词可以看到,Playwright正在积极拥抱AI智能体(Agent)生态。这为自动化带来了新的可能性。
- Playwright CLI (
@playwright/cli): 这是一个为AI编码助手(如GitHub Copilot、Cursor、Claude Code)优化的命令行工具。它允许AI直接通过自然语言指令操作浏览器。例如,你可以对AI说:“用playwright-cli打开某个页面,填写表单并截图”。AI会生成相应的CLI命令序列。它的命令比直接调用API更简洁,token效率更高。 - Playwright MCP (Model Context Protocol) Server (
@playwright/mcp): 这是更深入的一步。MCP是一个让AI模型安全使用外部工具的协议。Playwright MCP Server将浏览器控制能力暴露给AI智能体。AI看到的不是一个像素图像,而是页面的结构化可访问性树,从而能更准确、更确定性地操作元素(通过元素引用,如e5)。这对于构建复杂的AI自动化工作流非常有用。
对于大多数个人开发者或测试团队,目前主力仍是Playwright Test和Library。但了解CLI和MCP能让你看到未来“自然语言编程”和“AI驱动自动化”的雏形,保持技术视野的前沿性。
从我个人的使用体验来看,Playwright极大地提升了编写和维护Web自动化脚本的幸福感。它的设计处处体现了对开发者体验的重视。对于零基础的朋友,我的建议是:不要被它众多的功能吓到,先从
npm init playwright@latest开始,运行一下示例,然后用CodeGen录一个你自己常逛网站的操作,看看生成的代码。接着,尝试修改这些代码,用更稳定的定位器替换它。当你成功运行第一个自己写的测试时,你就已经上车了。剩下的,就是在实际项目中不断实践和探索这个强大工具的更多可能性。记住,好的工具是让你更专注于解决问题本身,而不是与工具搏斗。Playwright正是这样一件利器。
