AI驱动自动化测试:Playwright与AI工具融合实践指南

📅 2026/7/2 22:30:02 👁️ 阅读次数 📝 编程学习
AI驱动自动化测试:Playwright与AI工具融合实践指南

1. 项目概述:当AI撞上自动化测试,脚本的“永生”之路

最近在测试圈子里,一个话题的热度居高不下:AI驱动的自动化测试。特别是当像Playwright这样的现代测试框架,遇上了Cursor、通义灵码这类AI编程工具,很多同行都在讨论,我们写了多年的测试脚本,是不是终于要迎来一次“革命”,甚至走向某种意义上的“永生”了?所谓“永生”,并不是说代码永不修改,而是指脚本的维护成本大幅降低、适应能力极大增强,能够随着业务变化而“自我进化”。作为一个在自动化测试领域摸爬滚打了十多年的老兵,我亲眼见证了从QTP到Selenium,再到如今的Playwright,工具的迭代背后,是测试工程师对效率和质量永无止境的追求。而AI的加入,正在将这种追求推向一个新的高度。

这系列文章,我就想和你聊聊这个话题,并且手把手地带你从零开始,深入Playwright的世界。我们不止要学怎么用,更要探讨如何用AI来“加持”它,让编写、维护测试脚本这件事,变得前所未有的高效和智能。无论你是刚接触自动化测试的新手,还是正在为维护庞大而脆弱的Selenium脚本库而头疼的资深工程师,我相信接下来的内容都能给你带来新的思路和可以直接上手的工具。我们将从最基础的安装和环境配置讲起,逐步深入到核心API、最佳实践,并最终探索如何结合AI工具,构建一个能够“理解”需求、“生成”并“修复”测试用例的智能工作流。你会发现,测试脚本的“永生”或许不是幻想,而是一个正在发生的、可实践的工程进化。

2. Playwright核心优势与生态解析

2.1 为什么是Playwright?多维度对比下的技术选型

在决定深入一个技术之前,我们必须先搞清楚它解决了什么痛点。市面上UI自动化测试框架不少,老牌的有Selenium,移动端有Appium,那为什么Playwright能在近几年异军突起,成为众多开发者和测试工程师的新宠?我通过一个多维度的对比表格,并结合实际项目中的踩坑经验,来为你拆解。

特性维度Selenium (WebDriver)Playwright核心差异与影响
架构与协议基于W3C WebDriver标准,通过浏览器驱动与浏览器通信。使用CDP、WebSocket等现代协议直接与浏览器内核通信。根本性差异。WebDriver协议是“翻译层”,指令传递慢且不稳定。Playwright直连内核,速度更快,指令更底层,稳定性极大提升。
浏览器支持支持所有主流浏览器,但需要单独下载和匹配对应版本的驱动。开箱即用,playwright install命令自动下载Chromium, Firefox, WebKit的适配版本。体验碾压。Selenium的版本匹配是经典噩梦,一个Chrome升级可能导致整个测试套件崩溃。Playwright的版本绑定机制彻底解决了这个问题。
自动等待需要显式编写等待(如WebDriverWait),否则极易因元素未加载而报错。内置智能等待。大部分操作(如click,fill)会自动等待元素可操作状态。稳定性分水岭。这是Playwright减少“Flaky Tests”(不稳定的测试)最关键的 design。新手不用再被各种NoSuchElementException折磨。
网络拦截与Mock可通过代理或浏览器插件实现,但配置复杂,功能有限。原生强大支持。可轻松拦截、修改网络请求,模拟API响应,录制HAR文件。赋能测试场景。对于前端分离架构,可以直接在测试中Mock后端API,实现前后端并行开发测试,或模拟异常网络情况。
多标签页/上下文支持,但API较为原始,上下文切换需要手动管理。第一公民支持BrowserContext概念清晰,可完全隔离Cookie、缓存,轻松模拟多用户、隐身会话。适合复杂场景。做电商测试时,用一个浏览器实例模拟用户A登录加购,用户B浏览,非常方便。
移动端模拟通过Chrome DevTools Protocol可进行有限模拟。设备模拟器。提供一批主流移动设备(如iPhone, Pixel)的视口、User-Agent、触摸屏等参数预设。跨端测试便利。一行代码切换设备配置文件,即可测试响应式布局或移动端交互,无需启动真机或模拟器。
录制与代码生成有Selenium IDE等插件,但生成代码质量一般,维护性差。强大的Codegenplaywright codegen命令启动一个交互式浏览器,你的操作实时生成高质量、可维护的测试代码(支持多种语言)。学习与迁移利器。不仅是新手学习API的神器,也是将现有手工测试用例快速转化为自动化脚本的捷径。

从我实际迁移项目的经验来看,从Selenium转向Playwright,最直接的感受是脚本稳定性提升了70%以上。以前需要大量time.sleep和显式等待来“安抚”的测试用例,现在大多能稳定运行。其根本原因就在于上述的架构优势。此外,Playwright的官方文档非常优秀,并且其团队(来自微软)维护积极,生态也在快速扩展,比如现在热门的Playwright MCP,就是将其能力通过标准协议暴露给AI智能体,这正是实现“AI驱动”的基石。

2.2 Playwright生态与AI融合的现状

Playwright不仅仅是一个测试库,它正在成长为一个平台。理解其生态,能帮助我们更好地利用它,尤其是与AI结合。

  1. Playwright Test Runner:这是官方推荐的测试运行器。它不是一个独立的框架,而是基于你熟悉的测试框架(如Jest, Mocha, AVA)的包装,提供了专为Playwright优化的夹具、断言和报告。它的fixture(如page,context)管理方式非常优雅,是编写可维护测试代码的关键。
  2. Playwright CLI:命令行工具集,是日常开发的瑞士军刀。除了上面提到的codegen,还有:
    • playwright test:运行测试,支持并行、重试、录制视频/截图、生成HTML报告。
    • playwright show-trace:可视化调试神器。当测试失败时,会生成一个trace.zip文件,用这个命令打开,可以像看录像一样回溯测试的每一步操作、网络请求、控制台日志,定位问题效率极高。
  3. 与AI工具的集成:这正是“革命”的发生地。
    • Cursor / VS Code + MCP:MCP(Model Context Protocol)是一个让AI模型安全、结构化地使用工具的标准。Playwright MCP服务可以将浏览器控制、页面信息查询等能力暴露给Cursor或VS Code中的AI助手(如Claude)。你可以直接对AI说:“帮我在当前页面点击登录按钮,然后检查用户名是否显示”,AI能通过MCP调用Playwright执行操作并返回结果。这实现了自然语言到测试执行的直接转换
    • 通义灵码 / GitHub Copilot:这类代码补全AI,能基于你对Playwright API的使用模式,智能生成后续代码片段。例如,你写下await page.goto,它可能自动补全一个常用URL;你开始写一个选择器,它能提示完整的CSS或XPath。这大大提升了编码速度。
    • 专用AI测试工具:如TestDino等,它们可能直接集成了Playwright,允许你通过描述测试场景来生成完整的测试脚本。

注意:AI生成代码目前还不能完全替代工程师。它生成的代码可能存在选择器不够健壮、缺乏必要的断言或错误处理等问题。AI的最佳角色是“高级助手”,负责初稿生成和重复性编码,而工程师负责审核、优化和注入业务逻辑与稳定性策略。直接使用AI生成的脚本投入CI/CD,风险很高。

3. 从零开始:Playwright环境搭建与核心API实战

3.1 一站式环境配置与项目初始化

理论说了这么多,我们直接上手。这里以Python环境为例(Node.js步骤类似),因为Python在测试和数据处理领域应用更广,且与AI工具链结合也更紧密。

第一步:安装Python与包管理工具确保你的系统安装了Python 3.7+。推荐使用pyenvconda管理多版本Python。使用pip作为包管理工具。

第二步:创建并进入项目目录

mkdir playwright-ai-demo && cd playwright-ai-demo

第三步:使用虚拟环境(强烈推荐)虚拟环境能隔离项目依赖,避免全局包冲突。

# 创建虚拟环境 python -m venv venv # 激活虚拟环境 # Windows: venv\Scripts\activate # macOS/Linux: source venv/bin/activate

激活后,命令行提示符前会出现(venv)标识。

第四步:安装Playwright

pip install playwright

安装完成后,安装Playwright所需的浏览器内核。这是Playwright便利性的体现:

playwright install

这个命令会下载Chromium、Firefox和WebKit的适配版本,存放在用户目录下,与你的代码版本绑定,无需手动管理驱动。

第五步:安装测试运行器Playwright Test可以和Pytest完美结合,我们安装Pytest和Playwright的Pytest插件。

pip install pytest pytest-playwright

第六步:验证安装创建一个最简单的测试脚本test_demo.py

import re from playwright.sync_api import Page, expect def test_has_title(page: Page): page.goto("https://playwright.dev/") # 使用Playwright内置的断言 expect(page).to_have_title(re.compile("Playwright")) def test_get_started_link(page: Page): page.goto("https://playwright.dev/") # 定位元素并点击 link = page.get_by_role("link", name="Get started") link.click() # 断言URL变化 expect(page).to_have_url(re.compile(".*/docs/intro"))

运行测试:

pytest test_demo.py --headed # --headed 表示打开浏览器界面,默认是无头模式

如果看到浏览器打开,自动执行操作并全部通过,恭喜你,环境搭建成功!

实操心得:我建议在项目初期就配置好pytest.ini文件,来设置一些默认选项,比如设置默认超时时间、指定测试目录、自动加载插件等,这能让团队协作更顺畅。

[pytest] addopts = -v --tb=short --headed timeout = 30 testpaths = tests plugins = playwright

3.2 核心API深度解析与最佳实践

Playwright的API设计非常人性化,我们重点掌握几个核心概念和对应的API。

1. 浏览器、上下文与页面(Browser, Context, Page)这是Playwright的层级模型,理解它对于编写高效、隔离的测试至关重要。

  • Browser:对应一个浏览器进程(如Chrome)。通过playwright.chromium.launch()启动,消耗资源最多。
  • Context:浏览器上下文。可以理解为一个独立的会话,拥有独立的Cookie、LocalStorage、缓存和权限设置。一个Browser可以创建多个互不干扰的Context。这是实现多用户测试、平行测试的关键。
  • Page:一个标签页。一个Context可以有多个Page。我们的大部分交互都在Page上进行。
import asyncio from playwright.async_api import async_playwright async def main(): async with async_playwright() as p: # 启动浏览器 browser = await p.chromium.launch(headless=False) # 创建两个独立的上下文(模拟两个用户) context1 = await browser.new_context() context2 = await browser.new_context(viewport={'width': 1280, 'height': 720}) # 在每个上下文中创建页面 page1 = await context1.new_page() page2 = await context2.new_page() # 两个页面操作完全隔离 await page1.goto('https://example.com') await page2.goto('https://example.com') # ... 执行测试 ... await browser.close() asyncio.run(main())

2. 元素定位器(Locator)—— 稳定性的核心Playwright强烈推荐使用Locator API来定位元素,而不是直接使用page.$page.$$。Locator代表一个随时可以查找元素的策略,它内置了自动等待和重试机制。

最佳实践定位器优先级:

  1. get_by_role():按ARIA角色定位,如buttonlinktextbox。这是最推荐的方式,可访问性最好,最稳定。
    await page.get_by_role("button", name="Submit").click() await page.get_by_role("textbox", name="Username").fill("testuser")
  2. get_by_text() / get_by_label():按文本或标签文本定位。非常直观。
    await page.get_by_text("Welcome back").click() await page.get_by_label("Password").fill("secret")
  3. get_by_test_id():专门为测试添加的属性。需要在开发阶段配合,但一旦使用,是最健壮的定位方式,完全不受UI样式变化影响。
    # 前端代码:<button>await page.locator(".primary-btn.submit").click() # CSS await page.locator("//button[contains(text(), 'Save')]").click() # XPath

避坑指南:绝对避免使用基于索引的定位,如page.locator('button').nth(3),除非元素列表完全静态。UI顺序的微小变化就会导致测试失败。优先使用有语义的定位方式。

3. 断言(Assertions)—— 使用expectPlaywright提供了丰富的、专为异步操作优化的断言库expect。它也会自动等待条件满足。

from playwright.sync_api import expect # 断言页面标题 expect(page).to_have_title("Dashboard") # 断言URL包含某字符串 expect(page).to_have_url(re.compile(".*/dashboard")) # 断言元素可见/包含文本 expect(page.get_by_role("heading", name="Welcome")).to_be_visible() expect(page.locator(".alert")).to_contain_text("Success") # 断言元素属性 expect(page.get_by_role("checkbox")).to_be_checked()

4. 网络拦截与Mock这是Playwright的杀手级功能,让你能控制测试环境。

# 拦截所有请求,并修改其中一个 await page.route("**/api/user", lambda route: route.fulfill( status=200, content_type="application/json", body=json.dumps({"name": "Mock User", "id": 1}) )) # 或者,继续请求但修改响应 await page.route("**/*", lambda route: route.continue_())

4. 构建健壮测试框架:工程化实践与模式

4.1 项目结构设计与页面对象模型(POM)

当测试用例超过十几个时,良好的项目结构是维护性的生命线。我推荐以下结构,并结合经典的页面对象模型

playwright-project/ ├── conftest.py # Pytest全局配置,定义fixture ├── pytest.ini # Pytest配置文件 ├── requirements.txt # 项目依赖 ├── pages/ # 页面对象层 │ ├── __init__.py │ ├── base_page.py # 基础页面类,封装公共方法 │ ├── login_page.py # 登录页面 │ └── dashboard_page.py # 仪表板页面 ├── tests/ # 测试用例层 │ ├── __init__.py │ ├── test_login.py │ └── test_dashboard.py ├── fixtures/ # 自定义夹具或测试数据 │ └── test_data.py ├── utils/ # 工具函数 │ └── helpers.py └── reports/ # 测试报告(自动生成)

base_page.py示例:

from playwright.sync_api import Page class BasePage: def __init__(self, page: Page): self.page = page self.timeout = 30000 # 默认超时时间 def navigate(self, url): """公共导航方法,可添加统一等待或日志""" self.page.goto(url) # 例如,可以在这里添加一个等待页面加载完成的通用逻辑 self.page.wait_for_load_state("networkidle") def get_by_test_id(self, test_id): """封装定位器,方便统一修改""" return self.page.get_by_test_id(test_id) def take_screenshot(self, name): """截图并附上时间戳,用于失败调试""" import datetime timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") self.page.screenshot(path=f"screenshots/{name}_{timestamp}.png")

login_page.py示例:

from pages.base_page import BasePage class LoginPage(BasePage): # 定位器作为属性,便于维护 USERNAME_INPUT = "[data-testid='username']" PASSWORD_INPUT = "[data-testid='password']" LOGIN_BUTTON = "button:has-text('Sign In')" ERROR_MSG = ".error-message" def __init__(self, page): super().__init__(page) def login(self, username, password): """登录业务流程""" self.page.fill(self.USERNAME_INPUT, username) self.page.fill(self.PASSWORD_INPUT, password) self.page.click(self.LOGIN_BUTTON) def get_error_message(self): """获取错误信息""" return self.page.inner_text(self.ERROR_MSG)

test_login.py示例:

import pytest from pages.login_page import LoginPage from pages.dashboard_page import DashboardPage class TestLogin: @pytest.fixture(autouse=True) def setup(self, page): """每个测试用例前都打开登录页面""" self.login_page = LoginPage(page) self.login_page.navigate("https://example.com/login") def test_successful_login(self, page): """测试成功登录""" self.login_page.login("valid_user", "valid_pass") dashboard = DashboardPage(page) # 使用expect断言 from playwright.sync_api import expect expect(dashboard.welcome_message).to_be_visible() def test_failed_login(self): """测试失败登录""" self.login_page.login("wrong_user", "wrong_pass") error_text = self.login_page.get_error_message() assert "Invalid credentials" in error_text

这种模式将定位器页面操作方法测试逻辑分离。当UI发生变化时,你通常只需要修改pages/目录下的定位器,而不需要改动大量的测试用例文件,极大地提升了可维护性。

4.2 高级夹具(Fixtures)与依赖注入

Pytest的夹具是管理测试资源的利器。Playwright Test提供了page,context,browser等内置夹具。我们也可以创建自定义夹具。

conftest.py中定义全局夹具:

import pytest from playwright.sync_api import Playwright, Browser, BrowserContext, Page @pytest.fixture(scope="session") def playwright_instance(): """会话级别的Playwright实例""" from playwright.sync_api import sync_playwright with sync_playwright() as playwright: yield playwright @pytest.fixture(scope="session") def browser(playwright_instance): """会话级别的浏览器实例,所有测试共享一个浏览器进程""" browser = playwright_instance.chromium.launch(headless=True) # CI环境用无头 yield browser browser.close() @pytest.fixture(scope="function") def context(browser): """函数级别的上下文,每个测试用例独立,实现隔离""" context = browser.new_context( viewport={'width': 1920, 'height': 1080}, record_video_dir="videos/" # 自动录制视频 ) yield context context.close() @pytest.fixture(scope="function") def page(context): """函数级别的页面,最常用的夹具""" page = context.new_page() yield page page.close() @pytest.fixture def login_page(page): """直接返回初始化好的页面对象""" from pages.login_page import LoginPage return LoginPage(page) @pytest.fixture def authenticated_page(page, login_page): """返回已登录状态的页面对象,用于需要登录的测试""" login_page.navigate("/login") login_page.login("standard_user", "secret_sauce") # 可以在这里添加登录成功的断言 yield page # 如果需要,可以在这里执行登出清理

在测试中,你只需要在参数中声明需要的夹具,Pytest会自动注入:

def test_with_authenticated_user(authenticated_page): # authenticated_page 已经是一个登录后的页面对象 dashboard = DashboardPage(authenticated_page) # ... 测试仪表板功能 ...

这种依赖注入的方式让测试代码非常清晰,资源管理自动化,是编写大型测试套件的基石。

5. AI赋能:从脚本编写到智能维护的工作流革命

5.1 利用AI工具加速脚本编写与重构

现在,让我们引入AI这个“超级助手”。以下是我在日常工作中,结合Cursor(或VS Code + Claude Code)提升效率的具体场景。

场景一:使用AI生成页面对象和基础测试用例假设你需要为一个新的“用户设置”页面编写测试。你可以直接对AI说:

“用Python和Playwright,遵循页面对象模型(POM),创建一个UserSettingsPage类。它包含以下元素:一个显示用户名的标题h1,一个修改邮箱的输入框(># pages/user_settings_page.py from playwright.sync_api import Page, expect class UserSettingsPage: def __init__(self, page: Page): self.page = page self.username_heading = page.locator("h1") self.email_input = page.get_by_test_id("email-input") self.save_button = page.get_by_role("button", name="Save") def update_email(self, new_email: str): self.email_input.fill(new_email) self.save_button.click() # 可以添加等待成功提示的代码 self.page.wait_for_selector(".toast-success", state="visible", timeout=5000) def get_username(self) -> str: return self.username_heading.inner_text() # tests/test_user_settings.py def test_update_user_email(page): from pages.user_settings_page import UserSettingsPage settings_page = UserSettingsPage(page) # 假设已经导航到该页面 page.goto("/settings") old_name = settings_page.get_username() new_email = f"test_{old_name}@example.com" settings_page.update_email(new_email) # 验证:可以重新加载页面或检查成功消息 page.reload() # 假设邮箱会在输入框里回显 expect(settings_page.email_input).to_have_value(new_email)

生成后,你需要审查和优化:检查定位器是否最优(比如h1可能不唯一),添加更健壮的等待逻辑,补充必要的断言和错误处理。

场景二:使用AI解释错误并修复脚本当测试失败时,将错误日志和相关的代码片段丢给AI。

“我的Playwright测试失败了,错误信息是:Timeout 30000ms exceeded. waiting for selector \".modal-content\" to be visible。这是我的相关代码:

await page.click("#open-modal") await page.wait_for_selector(".modal-content", state="visible")

可能的原因是什么?如何修复?”

AI可能会分析:

  1. 点击#open-modal后,模态框可能不是同步出现,而是有动画或异步加载。
  2. 选择器.modal-content可能写错了,或者元素在iframe里。
  3. 页面可能在点击后发生了导航或重载。 并给出建议:增加超时时间、使用page.locator(...).wait_for()、检查网络请求、或者先确保点击成功。

场景三:使用Playwright MCP进行自然语言交互这是更前沿的用法。通过配置Playwright MCP服务器,AI可以直接控制浏览器。例如在Cursor中,你可以安装MCP插件并连接Playwright服务,然后直接在聊天框输入:

“打开浏览器,访问https://github.com,在搜索框里输入playwright并搜索,把第一页的仓库名字列给我。”

AI会通过MCP调用Playwright执行这些步骤,并返回结果。这为探索性测试快速编写端到端测试脚本提供了全新的交互模式。

5.2 实现“自愈”测试与智能分析

AI的更高阶应用是让测试脚本具备一定的“自愈”能力和分析能力。

1. 选择器失效的自动修复:我们可以编写一个钩子函数,当因为元素找不到导致测试失败时,尝试让AI分析当前页面DOM,推荐一个新的、更稳定的选择器。

# 这是一个概念性示例 def heal_locator(page, old_locator, action="click"): """ 尝试修复失效的定位器。 old_locator: 失败的选择器字符串 action: 试图执行的操作,如 'click', 'fill' """ # 1. 捕获当前页面的HTML快照和截图 html = page.content() screenshot_path = "error_snapshot.png" page.screenshot(path=screenshot_path) # 2. 将信息发送给AI API(如OpenAI GPT-4V,它能理解图片) prompt = f""" 网页交互失败。原选择器 `{old_locator}` 在执行 `{action}` 时未找到元素。 这是当前的HTML片段(围绕目标区域)和截图。 请分析并提供一个新的、更健壮的定位器建议(优先使用get_by_role, get_by_text, get_by_test_id)。 目标元素应该是一个用于{action}的交互元素。 HTML: {html[:5000]} # 截取部分HTML """ # 调用AI API,获取新的定位器建议 # new_locator_suggestion = call_ai_vision_api(prompt, screenshot_path) # 3. 记录建议,或尝试使用新定位器重试(需谨慎) # page.locator(new_locator_suggestion).click() # 将修复记录到日志或数据库

这只是一个思路,实现起来需要考虑成本、可靠性和循环风险,但它代表了未来自动化测试维护的一个方向。

2. 测试结果智能分析与报告:AI可以分析测试运行的历史数据、失败日志和截图,总结出失败模式。例如:

  • “最近5次失败,有4次与‘购物车结算’模块的网络超时有关,建议检查该接口性能或增加超时时间。”
  • “失败截图显示按钮样式变了,但># .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-python@v4 with: python-version: '3.10' - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt # 安装Playwright浏览器,使用CI专用参数 playwright install --with-deps chromium - name: Run your tests run: | # 运行测试,无头模式,生成HTML报告和追踪文件 pytest tests/ --headed=false --html=reports/report.html --self-contained-html - name: Upload test results if: always() # 即使测试失败也上传 uses: actions/upload-artifact@v4 with: name: playwright-report path: | reports/ test-results/ videos/ screenshots/ retention-days: 7

    关键点解析:

    • playwright install --with-deps chromium:在CI环境中,通常只安装一个浏览器(如Chromium)以加快速度。--with-deps会自动安装系统依赖。
    • --headed=false:在无服务器环境中必须使用无头模式。
    • --html=reports/report.html:使用pytest-html插件生成美观的HTML报告。
    • if: always()actions/upload-artifact:无论测试成功与否,都将报告、追踪文件、视频和截图打包上传,便于事后查看失败详情。

    6.2 常见问题排查与性能优化

    问题1:测试在CI上通过,本地却失败(或反之)。

    • 排查:99%是环境差异。检查CI和本地的浏览器版本(playwright --version)、视口大小、时区、语言环境、网络(如CI可能无法访问某些外部依赖)。
    • 解决:在playwright.config.ts或通过browser.new_context()统一配置上下文。固定视口语言是常用手段。
      context = await browser.new_context( viewport={'width': 1920, 'height': 1080}, locale='en-US', timezone_id='America/New_York' )

    问题2:测试不稳定(Flaky Tests),时而成功时而失败。

    • 排查:使用playwright show-trace命令打开失败时生成的trace.zip文件。这是最重要的调试工具。你可以逐帧查看操作、网络请求、控制台输出,精准定位是元素未出现、网络请求慢还是JS错误。
    • 解决策略
      1. 强化定位器:使用get_by_role,get_by_test_id等语义化定位器。
      2. 使用更精确的等待:避免sleep。用page.wait_for_selector(selector, state='attached'|'visible'|'hidden')locator.wait_for()
      3. 等待网络状态:在导航后使用page.wait_for_load_state('networkidle')
      4. 启用重试:在playwright.config.ts中配置retries,或在Pytest中使用@pytest.mark.flaky(reruns=3)
      5. 隔离测试:确保每个测试使用独立的BrowserContext,避免Cookie和缓存污染。

    问题3:测试运行太慢。

    • 优化
      1. 并行执行:Playwright Test原生支持并行。在CI中,可以通过pytest -n auto(需要pytest-xdist)或Playwright配置的workers选项来并行运行测试文件。
      2. 只安装必要浏览器:CI中只安装chromium
      3. 复用浏览器上下文:对于不需要完全隔离的测试组,可以尝试在模块或类级别共享context,但要注意清理状态(如清除Cookie)。
      4. 禁用不必要的录制:视频录制和追踪文件会消耗大量I/O和存储。仅在调试时开启,或只为失败的测试录制。
        # 在conftest.py的context fixture中根据环境变量判断 record_video = {"dir": "videos/"} if os.getenv("RECORD_VIDEO") == "true" else None context = browser.new_context(record_video_dir=record_video)

    问题4:如何处理文件下载?Playwright可以监听下载事件。

    # 开始下载前,设置下载路径并等待下载完成 async with page.expect_download() as download_info: await page.get_by_text("Download Report").click() download = await download_info.value # 指定保存路径 save_path = f"./downloads/{download.suggested_filename}" await download.save_as(save_path) print(f"File downloaded to: {save_path}")

    在我个人的实践中,将Playwright与AI结合,最大的改变不是减少了代码行数,而是改变了工作流的重心。我从一个重复的“脚本编写者”和“调试工”,变成了一个“场景设计者”和“策略制定者”。AI负责将我的自然语言描述和业务场景转化为代码初稿,而我则专注于设计更全面的测试用例、构建更稳定的测试框架、分析测试结果背后的质量风险。测试脚本本身,因为有了AI的辅助维护和智能分析,其生命周期被大大延长,维护成本显著下降,这或许就是迈向“永生”的第一步。这条路还很长,但工具已经就位,现在正是探索和实践的最佳时机。