当前位置: 首页 > news >正文

AI自动化测试入门:Python+Playwright+Pytest实战路线

AI自动化测试并不是让AI完全替代测试人员,而是把测试中最耗时、最容易重复的劳动交给AI辅助完成。真正有价值的能力是:知道哪些用例需要自动化、如何让脚本稳定运行、如何在页面变化时快速修复定位器、如何把测试结果接入工程体系。如果只有7小时,你需要一条比“从零学Selenium”更短的学习路径,因为传统自动化往往卡在元素定位、脚本维护和等待策略上。这篇文章围绕一个最小可落地的技术栈:Python + Playwright + Pytest + AI辅助工具,拆解从环境准备到持续集成的完整过程。

1. 先搞清楚AI自动化测试到底改了什么

1.1 传统自动化测试为什么“学会容易,维护难”

传统自动化测试的核心流程并不复杂:打开浏览器,定位页面元素,执行点击和输入,再对结果加断言。这条链路运行一次并不难,难的是当项目进入持续迭代之后,脚本为什么频繁失败、失败之后要花多长时间去修。

真实项目里,最常见的维护成本来自几个固定场景。页面结构调整后,基于CSS或XPath写死的定位器失效;前端加了异步渲染后,固定sleep等待不可靠;系统弹窗、iframe、新窗口、shadow DOM这些特殊情况需要大量样板代码;断言写得太简单,页面没报错但结果不对,脚本照样通过。这些问题的本质不是“代码写错了”,而是“脚本和页面结构绑得太紧”。

传统工具并不是没有价值。Selenium仍是许多企业的存量基础,Appium仍是移动端自动化的常见选择。但从学习效率来看,7小时里如果从传统Selenium脚本的录制回放一路学起,很可能还没有接触到测试设计和稳定性处理,时间就已经用完。更好的策略是:先选择一个自动等待更完善、API更现代的工具,跑通一个最小闭环,然后再回头理解传统方案。

1.2 AI介入自动化测试的三种模式

AI在自动化测试里的落地方式,并不等同于“AI自动生成测试用例然后替你把活都干了”。目前比较成熟且具备工程实用价值的有三种模式。

第一种是辅助生成代码。测试人员用自然语言描述目标操作,AI生成Playwright或Pytest代码。这个模式适合编写页面对象、定位器、数据驱动用例的初稿,节省的是“从需求到代码”的翻译成本。但生成的代码必须经过人工确认和真实环境验证。

第二种是智能定位。传统定位器依赖DOM结构,页面一改就崩。AI可以把一段HTML片段和自然语言目标交给大模型,让它推荐更稳定的定位方式,比如优先使用语义化的rolename>python -m venv .venv

macOS或Linux执行:

python3 -m venv .venv

然后激活虚拟环境。Windows下:

.venv\Scripts\activate

macOS或Linux下:

source .venv/bin/activate

激活后命令行前缀会出现(.venv),说明当前已经进入虚拟环境。此时再执行:

python --version

确认Python版本符合预期。这里要注意,如果激活虚拟环境后python仍然指向全局版本,说明激活命令没有生效,或者当前终端缓存了旧的命令路径。

注意:不要跳过虚拟环境这一步。很多“环境装不上”的问题,根源都是项目依赖和系统依赖混在了一起。

2.3 安装依赖和浏览器

在虚拟环境激活状态下,先升级pip,再安装核心依赖:

python -m pip install --upgrade pip pip install playwright pytest pytest-html requests

playwright是浏览器自动化核心,pytest是测试组织框架,pytest-html用于生成HTML报告,requests用于接口测试和调用AI服务。

安装完Python包之后,还需要下载Chromium浏览器内核:

playwright install chromium

这一步会在用户目录下载浏览器文件。如果在公司网络或受限环境中下载失败,需要检查网络权限,或者配置团队内部的镜像源。下载完成后,可以运行一条简单的验证命令:

python -c "from playwright.sync_api import sync_playwright; print('playwright ok')"

输出playwright ok,说明Python包可以正常导入。

2.4 初始化项目和pytest配置

先建立项目目录结构。建议从第一天就按照工程化方式组织,后面扩展才不会乱:

auto_test_project/ ├── config/ │ └── settings.py ├── pages/ │ └── base_page.py ├── tests/ │ ├── conftest.py │ └── test_first.py ├── utils/ │ ├── ai_client.py │ └── screenshot.py ├── reports/ ├── requirements.txt └── pytest.ini

config放URL、账号、超时时间等配置;pages放页面对象;tests放测试用例;utils放AI客户端、截图工具等公共能力;reports保存测试报告和失败截图。

pytest.ini中做基础配置:

[pytest] python_files = test_*.py python_classes = Test* python_functions = test_* addopts = -q --disable-warnings testpaths = tests

这里指定了测试文件、类、函数的命名规则,并让pytest只扫描tests目录。运行pytest时如果找不到用例,优先检查命名是否匹配这些规则。

最后创建一个最简单的空测试文件,例如tests/test_first.py

def test_example(): assert 1 + 1 == 2

运行:

pytest

看到测试通过后,环境准备这一步才算真正完成。

3. 用Playwright跑通第一个Web自动化用例

3.1 第一个端到端用例

有了环境后,先写一个不依赖任何被测系统的用例,验证Playwright本身是否正常。这里以https://example.com为例,这个页面结构稳定,适合做环境验证。实际项目里应该把URL替换为被测系统地址。

创建tests/test_first.py,写入:

from playwright.sync_api import sync_playwright def test_open_demo_page(): with sync_playwright() as p: browser = p.chromium.launch(headless=False) page = browser.new_page() page.goto("https://example.com") assert page.title() == "Example Domain" browser.close()

代码里with sync_playwright() as p负责启动和清理Playwright运行时。headless=False会弹出浏览器窗口,便于新手观察操作过程。如果是在CI或服务器上运行,需要改成headless=True

运行用例:

pytest tests/test_first.py

如果浏览器没有自动关闭,检查是否在断言失败时抛出了异常。更健壮的写法是使用try/finally或后置fixture释放资源,而不是只靠脚本正常结束时关闭。

3.2 用pytest fixture封装浏览器生命周期

手写browser.close()在遇到断言失败时容易漏执行。推荐的方案是把浏览器和页面的创建放在pytest的fixture里,pytest会保证清理。

tests/conftest.py中定义:

import pytest from playwright.sync_api import sync_playwright @pytest.fixture(scope="session") def browser(): with sync_playwright() as p: browser = p.chromium.launch(headless=True) yield browser browser.close() @pytest.fixture() def page(browser): context = browser.new_context() page = context.new_page() yield page context.close()

这里browser是session级别,整个测试会话只创建一次浏览器实例;page是function级别,每个用例都有独立的浏览器上下文,避免用例之间的登录态和Cookie相互污染。

修改测试用例,让模板自动管理页面:

def test_demo_title(page): page.goto("https://example.com") assert page.title() == "Example Domain"

这样即使有10个用例,每个用例也会拿到干净的新页面。这个设计对web自动化非常重要,因为页面状态共享往往是“偶发失败”的来源。

3.3 用AI辅助生成定位器和用例

当页面结构复杂时,人工写定位器很容易写出非常脆弱的表达式。AI在这里可以帮上忙,但不是直接让它给你一段选择器就算完。

提供一个通用提问模板:

页面地址:<这里填被测页面URL> 目标操作:<例如:点击登录按钮> 页面HTML片段:<打开开发者工具,复制目标元素附近的一小段HTML> 请生成Playwright定位器,要求优先使用get_by_role,其次data-testid,最后才是文本。只输出代码,不要解释。

假设页面HTML是:

<button class="btn btn-primary login-button" type="submit">登录</button>

AI可能返回:

page.get_by_role("button", name="登录")

对比page.locator("button.btn.btn-primary.login-button")get_by_role的语义更稳定,页面class调整后它仍然能工作。AI生成的结果会不会出错?会。所以必须拿到真实页面上去运行一遍,确认能找到唯一元素。

注意:不要直接复制AI生成的选择器到测试代码里。先运行一次,再检查是否匹配到了预期元素,最后再固化到页面对象中。

3.4 参数化与数据驱动

同一个业务流程往往要用多组数据验证,比如登录时要验证正常账号、错误密码、空用户名。把这些场景复制成多条用例会非常啰嗦,pytest的parametrize可以解决。

示例写法:

import pytest @pytest.mark.parametrize("keyword, expected_title", [ ("python", "python"), ("playwright", "playwright"), ]) def test_search(page, keyword, expected_title): page.goto("https://example-custom-site.com") search_box = page.get_by_role("textbox", name="搜索") search_box.fill(keyword) search_box.press("Enter") assert expected_title in page.title()

这里的URL和业务字段只是示例,落地时要结合真实被测系统调整。参数化的好处是,新增一组测试数据只需要增加一行元组,不需要复制用例函数。但也要注意,不是所有数据都适合参数化。如果每一组数据都对应不同的断言逻辑,强行参数化反而会降低代码可读性。

4. 把AI能力接进测试:从“生成代码”到“自适应维护”

4.1 明确AI在测试项目里的边界

在把AI接入项目代码之前,先定清楚哪些环节可以交给AI,哪些不能。

适合AI辅助的场景不适合完全交给AI的场景
从HTML生成定位器对高风险的支付、删除等操作决策
生成参数化测试数据初稿断言业务结果是否符合复杂规则
汇总失败日志和截图,生成分析摘要对测试覆盖率的最终评估
补充常规边界值测试数据判断产品需求是否发生了变化

在测试项目里,AI更适合做“翻译”和“归纳”,不太适合做“决策”。这一点要在团队协作时讲清楚,否则会出现AI生成的脚本破坏了业务流程的严重问题。

4.2 用Python封装一个AI客户端

为了让AI能力可以在多个用例里复用,建议封装一个统一的客户端。下面这个示例假设你已经有可访问的大模型服务,API格式为常见的chat/completions格式,实际项目以服务方文档为准。

创建utils/ai_client.py

import os import requests LLM_API_URL = os.getenv("LLM_API_URL", "http://your-llm-endpoint/v1/chat/completions") LLM_API_KEY = os.getenv("LLM_API_KEY", "") def chat(prompt: str, system: str = "你是一名自动化测试专家。") -> str: payload = { "model": os.getenv("LLM_MODEL", "local-model"), "messages": [ {"role": "system", "content": system}, {"role": "user", "content": prompt}, ], "temperature": 0.2, } headers = {"Authorization": f"Bearer {LLM_API_KEY}"} resp = requests.post(LLM_API_URL, json=payload, headers=headers, timeout=30) resp.raise_for_status() return resp.json()["choices"][0]["message"]["content"]

这段代码的关键点有三个。temperature设置为0.2,目的是让AI输出尽量稳定,不要每次都生成不同写法;超时设置为30秒,避免因为模型响应慢拖挂测试;API地址和密钥通过环境变量注入,不要写死在代码里。

4.3 用AI辅助定位元素,并在常规定位失败时兜底

AI定位不能作为默认主路径,否则每次跑测试都要等待模型响应,测试速度会慢到无法接受。合理做法是:常规定位失败后,记下日志,再使用AI辅助定位作为兜底。

先让AI返回结构化描述,不要直接返回代码字符串。创建一个定位器生成函数:

import json def generate_locator(html_snippet: str, target: str) -> dict: prompt = f""" 请分析下面的HTML片段,找出目标元素:{target} HTML: {html_snippet} 请返回JSON,格式为: {{"type": "role", "role": "button", "name": "登录"}} 或 {{"type": "test_id", "test_id": "login-btn"}} 或 {{"type": "text", "text": "登录"}} 或 {{"type": "selector", "selector": "button.login-btn"}} 只输出JSON。 """ content = chat(prompt) return json.loads(content) def locator_from_desc(page, desc: dict): locator_type = desc["type"] if locator_type == "role": return page.get_by_role(desc["role"], name=desc.get("name")) if locator_type == "test_id": return page.get_by_test_id(desc["test_id"]) if locator_type == "text": return page.get_by_text(desc["text"]) return page.locator(desc["selector"])

在用例里这样使用:

from playwright.sync_api import expect from utils.ai_client import generate_locator, locator_from_desc def test_login_with_ai_fallback(page): page.goto("https://your-test-site.com/login") try: page.get_by_role("button", name="登录").click(timeout=3000) except Exception as exc: html_snippet = page.locator("body").inner_html()[:3000] desc = generate_locator(html_snippet, "登录按钮") locator = locator_from_desc(page, desc) expect(locator).to_be_visible() locator.click()

这里的兜底逻辑要有两个限制。第一,body的HTML可能非常大,传给模型前先截断,避免超长文本;第二,AI调用失败时要继续抛出原始异常,不能让测试因为AI服务波动而误判。

4.4 非预期弹窗导致失败的解决方案

接口自动化之外,Web自动化最常遇到的稳定性问题就是非预期弹窗。弹窗可能来自业务通知、广告、新版本提示,也可能来自浏览器原生弹窗。处理的原则是:不要一看到弹窗就关,而是先判断弹窗类型,再用对应的策略处理。

原生对话框由浏览器控制,例如alertconfirmprompt。Playwright可以通过监听dialog事件处理:

page.on("dialog", lambda dialog: dialog.accept())

页面内弹窗则是普通DOM元素,常见做法是在点击目标元素前,尝试关闭已知的弹窗按钮:

def close_known_popups(page, texts=("知道了", "我知道了", "关闭", "确定")): for text in texts: button = page.get_by_role("button", name=text) try: if button.is_visible(timeout=500): button.click(timeout=500) except Exception: pass

这个方案适用于弹窗文案已知且稳定的系统。如果弹窗变化非常频繁,建议保留每次失败的截图,再交给AI分析弹窗类型,逐步整理成可维护的弹窗处理列表。

4.5 用截图和AI做失败分析

用例失败时,最不希望看到的是测试报告里只有一句红色的报错。可以结合Pytest的hook,在失败时自动截图并保存当前页面HTML。

conftest.py中添加:

import pytest @pytest.hookimpl(hookwrapper=True) def pytest_runtest_makereport(item, call): outcome = yield report = outcome.get_result() if report.when == "call" and report.failed: page = item.funcargs.get("page") if page: page.screenshot(path=f"reports/{item.name}.png") with open(f"reports/{item.name}.html", "w", encoding="utf-8") as f: f.write(page.content())

有了截图和HTML快照,AI才能做进一步分析。可以写一个分析Prompt:

这是测试用例失败时的HTML内容。 请分析页面是否出现了非预期弹窗、元素未加载、页面跳转失败等情况。 给出可能的失败原因和下一步检查建议。

失败分析的意义不在于AI直接给出“某某代码错了”的结论,而是帮助测试人员从大量信息里快速定位到“弹窗”“加载慢”“选择器过时”这几类最常见原因。

5. 从浏览器测试扩到接口测试,再接入持续集成

5.1 接口自动化测试的最小闭环

UI自动化适合验证关键用户流程,但接口层才是测试覆盖率的重要来源。接口测试不需要打开浏览器,运行速度快,定位问题也更直接。

创建一个简单的接口测试示例,假设被测系统有一个登录接口和一个获取用户信息的接口:

import requests BASE_URL = "http://your-service-address" def test_login_and_get_profile(): login_resp = requests.post( f"{BASE_URL}/api/login", json={"username": "admin", "password": "123456"}, timeout=10, ) assert login_resp.status_code == 200, f"登录失败: {login_resp.text}" token = login_resp.json()["data"]["token"] assert token, "登录响应中没有token" profile_resp = requests.get( f"{BASE_URL}/api/profile", headers={"Authorization": f"Bearer {token}"}, timeout=10, ) assert profile_resp.status_code == 200 assert profile_resp.json()["data"]["username"] == "admin"

这里断言了三个关键点:登录接口的HTTP状态码、token是否返回、token能否正常访问用户信息。实际项目的接口字段可能不同,但这条“登录 → 拿token → 带token访问资源”的链路非常常见。接口测试报告里要区分“服务未启动”“接口返回500”“断言失败”三种情况。

5.2 用AI生成接口测试用例的工作流

接口测试用例的重复性很高,适合用AI辅助生成初稿。可以整理一个接口文档模板给AI:

接口路径:POST /api/login 请求体:{"username": "string", "password": "string"} 成功响应:{"code": 0, "data": {"token": "string"}} 失败响应:{"code": 1001, "message": "用户名或密码错误"} 请生成pytest参数化用例,覆盖正常登录、错误密码、缺少用户名、空密码四种情况。

AI生成的用例可能很完整,但也可能生成假设性的字段或断言,所以必须做两件事:第一,根据真实接口文档修正字段;第二,在本地至少运行一次所有生成用例,确认不是“纸面代码”。接口用例可以放在tests/test_api_demo.py里,和UI用例共存。

5.3 生成HTML测试报告

本地跑通之后,需要让测试结果看起来直观。pytest-html在上一步已经安装,只需在运行时加上参数:

pytest --html=reports/report.html --self-contained-html

--self-contained-html会把CSS和JS嵌入HTML文件,单文件可以直接发送给同事查看。报告里会显示每个用例的状态、耗时和失败信息。如果配合了失败截图,就可以在上报时定位问题。

执行后再打开reports/report.html,确认没有缺失样式,用例状态正常。

5.4 把测试放进CI,让每次提交都自动跑

本地能跑通只是第一步,自动化测试的真正价值在于持续运行。下面以GitHub Actions为例,提供一个最小CI配置。如果团队使用Jenkins、GitLab CI或自建平台,逻辑类似。

name: Automated Test on: push: pull_request: jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: '3.11' - name: Install dependencies run: | pip install -r requirements.txt playwright install --with-deps chromium - name: Run pytest run: pytest --html=reports/report.html --self-contained-html - name: Upload report if: always() uses: actions/upload-artifact@v4 with: name: test-report path: reports/

这里有一个关键点:CI环境没有显示器,Playwright必须以无头模式运行。如果用例里写死了headless=False,在CI中会报错。建议在fixture里通过环境变量控制:

import os @pytest.fixture(scope="session") def browser(): is_headless = os.getenv("HEADLESS", "true").lower() == "true" with sync_playwright() as p: browser = p.chromium.launch(headless=is_headless) yield browser browser.close()

5.5 全部跑通后的验收清单

一个可以拿给别人演示的自动化测试项目,至少要满足这几点:

  • 新环境按requirements.txt安装依赖后,能通过pytest一键运行。
  • 测试用例有清晰的文件和函数命名,别人能看懂每个用例在验证什么。
  • 失败用例能自动生成截图和页面HTML快照。
  • 生成的HTML测试报告可以离线打开。
  • README里写清了如何启动被测服务、如何运行测试、如何查看报告。

如果上面任意一项缺失,建议不要急着继续加测试用例,先把工程基础补齐。

6. 常见问题排查:从现象到根因,十分钟定位

6.1 环境装不上或版本不匹配

不少初学者在最开始就卡住。常见现象是pip install playwright成功,但运行测试时报ModuleNotFoundError,或者playwright install下载浏览器失败。

这可能是因为pip安装了Python包,但Playwright浏览器没有下载成功;也可能是激活的虚拟环境和执行命令的终端不一致。先检查:

python -m playwright --version which python pip show playwright

如果which python显示的不是项目虚拟环境路径,说明终端没有激活虚拟环境。如果playwright包存在,但浏览器下载失败,需要重新运行:

playwright install chromium

6.2 浏览器启动失败

在服务器或Docker容器中运行,经常遇到缺少系统依赖的错误。Playwright官方提供了自动安装依赖的命令:

playwright install-deps chromium

这个命令需要管理员权限。如果运行后仍然失败,检查操作系统版本是否受支持,以及是否有安全策略限制了用户目录下载文件。

6.3 定位器找不到元素

定位器报错是最常见的自动化测试问题。不要急着改定位器,先按顺序排查:

  1. 页面是否已经加载到目标元素。如果页面有异步渲染,先确认已经触发等待,例如expect(page).to_have_title()page.wait_for_selector()
  2. 元素是否在iframe里。Playwright需要使用frame_locator进入iframe再定位。
  3. 元素是否在shadow DOM中。Playwright可以穿透shadow DOM,但必须先确保定位的是正确的宿主节点。
  4. 页面是否发生了跳转或弹窗遮挡。结合上一步的截图判断。

6.4 AI生成的代码不可靠

AI生成的定位器可能在本地能跑,但过一天就失效,尤其是依赖文本、CSS class这些变化频繁的属性。AI输出的建议必须经过人工审查,核心判断标准是:这个定位器是否表达了元素的语义,而不是表面样式。

如果AI返回的是一个超长CSS路径,大概率不可维护。建议继续向AI追加提示,要求改用get_by_role>

http://www.cnnetsun.cn/news/4295759.html

相关文章:

  • ASP源码解析:校无忧网上报修系统架构、安全与现代化改造
  • AI测试实战:用Skill+Playwright构建Web自动化测试体系
  • Python与PyCharm安装全攻略:从环境变量到第一个项目运行
  • 腾讯2015春招移动客户端开发面试题核心考点解析
  • 从投递到拿offer:BAT实习面试全流程实战指南
  • 第04章 C类型、运算符和表达式(2):揭示内存背后的秘密——变量名、常量与声明的本质
  • 扫描Git仓库中的LLM推理痕迹:构建Aileaks类安全扫描器
  • python的图论工业场景模拟第十四篇:基于NetworkX与Matplolib图可视化模板构建,任务:设计并封装一个统一风格的画图函数,节点颜色映射度数,边粗细映射权重,避免标签重叠,图建模说明:确
  • 温州市本地维修壁挂炉师傅|上门维修壁挂炉电话|故障码不点火维修|本地口碑维修推荐
  • STM32WB55 SafeBoot烧录报错排查:RDP写保护与解锁实战
  • STM32U5并口屏驱动实战:FMC与GPDMA 2D寻址方案解析
  • 从OTAmatic获奖看车载OTA平台架构与工程实践要点
  • 基于Seq2Seq模型的Web攻击检测系统:从NLP到AI安全的工程实践
  • 传感器接口IC如何攻克生物化学传感的微弱信号难题?
  • 混合RL Rollout调度:超越Prefix Locality的推理优化实践
  • 产品岗笔试通关指南:题型拆解、答题框架与时间分配全攻略
  • LLM输出随机性解析:温度、种子与垂直AI稳定性实践
  • 解析pro文件
  • 一个 关于 椒盐 的 笑话
  • 基于pandas apply的文本预处理函数设计与DataFrame应用实践
  • C++模板与泛型编程:从《C++ Primer》习题解析到工业级代码实践
  • 手把手搭建反AI电脑:本地优先与数据隐私实践
  • 网易校招研发笔试复盘:数据结构与算法考点全解析
  • 百度前端秋招笔试复盘:从JS原理到算法题型的备考指南
  • Python数据分析与建模实战:从美赛C题到完整项目工作流
  • 阿里云秋招笔试深度拆解:从基础到云原生的备考指南
  • 阿里云研发岗秋招笔试复盘:从算法到工程实战的全面解析
  • STM32 MotionGR手势识别库:从配置到移植的完整实战指南
  • select为什么只能处理1024个连接?从源码到排障彻底讲透
  • 全国地貌shp矢量数据实操指南:从加载到转换全解析