
这次我们直接聊一套能落地的组合Pytest Skills MCP 驱动 AI 做 Web 自动化。视频标题是“90分钟只讲干货”文章这边也保持同样的节奏——不讲概念史不铺垫行业趋势上来就讲清楚这套技术栈能干什么、怎么配、怎么跑、怎么排查。先说结论MCPModel Context Protocol模型上下文协议解决的是“AI 能不能操作工具”的问题Pytest 解决的是“用例怎么组织、怎么断言、怎么输出报告”的问题Skills 解决的是“AI 生成用例时怎么不瞎写、不跑偏”的问题。三者合在一起AI 就不再只是帮你写一段一次性脚本而是能持续生成、执行、修复、维护 Web 自动化用例的工作流。这篇文章会覆盖这三个技术在 Web 自动化里各自负责什么、本地环境怎么准备、MCP Server 怎么用 Python 快速搭一个、Pytest 的 fixture 怎么设计、怎么把 MCP 工具接入 AI 对话流程、怎么用 Skills 约束 AI 的测试思路最后给出一套可复用的排查清单。文章里所有代码都按“能复制、能改、能跑”的标准写路径和接口名需要按你的实际项目替换。1. 核心能力速览能力项说明核心组成Pytest用例框架 MCP工具调用协议 SkillsAI 行为约束解决的核心问题AI 生成测试脚本后如何稳定执行、失败如何自愈、经验如何沉淀MCP 的作用让 AI 能调用浏览器、文件系统、接口服务等外部工具Pytest 的作用管理用例生命周期提供断言、固件、参数化、报告输出Skills 的作用把测试经验写成 AI 可读的规范文档约束生成质量和执行策略适用测试类型Web UI 自动化、接口自动化、回归测试、冒烟测试硬件门槛无特殊 GPU 要求普通开发机即可启动方式命令行启动 pytest / 通过 AI 对话触发测试 / MCP Server 独立运行是否支持 APIMCP 本身是协议支持通过 HTTP/SSE 暴露工具服务是否支持批量任务支持Pytest 天然支持批量执行也可以配合 CI 任务队列适合读者测试开发工程师、后端转测试、AI 编程工具重度使用者这套组合最大的价值不是“替代测试工程师”而是把重复劳动压到最低。AI 负责生成主流程用例Pytest 负责执行和校验MCP 负责让 AI 在需要时直接查页面状态、读接口数据、操作浏览器Skills 负责保证每次生成都遵循同一套测试规范。2. 这套组合要解决的实际问题先说痛点。传统的 Web 自动化测试有三个让人头大的环节第一写用例慢。一个登录流程从定位元素到处理等待、异常分支纯手写至少 30 到 50 行代码90% 时间花在查选择器和调等待上。第二维护成本高。前端稍微改个 class 名用例就红一片。定位元素失败后测试工程师要手动打开页面、打开 DevTools、找新选择器、改代码非常消耗精力。第三经验不沉淀。每个项目都有一堆“隐性规则”哪些按钮要用显式等待、哪些弹窗要特殊处理、哪些接口数据要 mock。这些规则散落在个人脑子里新人接手时一问三不知。Pytest Skills MCP 的搭配刚好打在这三个痛点上AI 帮你生成用例骨架元素定位和断言逻辑先跑起来节省写代码的时间。MCP 让 AI 能直接“看到”页面状态和接口响应。用例失败时AI 可以调用 MCP 工具重新拉取页面 DOM、查看接口返回、检查元素是否存在再决定是修选择器还是等更长时间。Skills 把团队测试规范变成 AI 可读的 Markdown 文档。AI 生成用例前先读 Skills生成的代码风格和策略就会和团队保持一致。再直白一点以前是你写代码让程序去测网页现在是 AI 写代码 AI 调用工具看现场 AI 根据现场反馈改代码Pytest 负责保证整个过程可重复、可验证、可报告。3. 原理拆解Pytest、MCP、Skills 各自负责什么3.1 Pytest用例框架层Pytest 在整个体系里是最容易理解的一环。它不负责怎么驱动浏览器也不负责 AI 怎么思考它只做一件事把测试用例组织起来按顺序或按标记执行给出断言结果和报告。在 AI 自动化场景里Pytest 承担四个职责用例结构标准化。所有 AI 生成的测试都按test_*.py组织函数名以test_开头Pytest 自动收集。fixture 提供运行环境。比如浏览器实例、登录状态、测试数据、临时目录都可以放在 fixture 里复用。断言与失败捕获。断言失败时Pytest 会输出详细的上下文信息AI 可以根据这些信息判断下一步动作。报告输出。支持 JUnit XML、HTML、Allure 报告方便对接 CI 和 AI 分析。3.2 MCP工具调用层MCP 是 Anthropic 在 2024 年底推出的开放协议后来被 OpenAI、Google 等逐步接纳目前已经成为 AI 工具调用的主流标准之一。它的核心思想很简单把外部能力抽象成一个个工具AI 通过统一的协议去发现和调用这些工具。一个 MCP 架构里有三个角色MCP Server负责提供工具能力。比如“获取当前页面标题”“读取文件”“执行 Shell 命令”。MCP Client嵌入在 AI 客户端里比如 Claude Desktop、Cursor、CodexAI 通过 Client 发现 Server 提供的工具列表。协议层定义工具如何描述、参数如何传递、结果如何返回常见传输方式有 stdio 和 HTTP/SSE。在 Web 自动化场景里MCP Server 可以提供这些工具browser_open(url)打开指定页面 browser_get_content()获取当前页面正文 browser_get_dom()获取当前页面 DOM 结构 browser_click(selector)点击指定元素 browser_input(selector, text)向输入框填入文本 browser_screenshot()截取当前页面 http_get(url)发送 GET 请求 http_post(url, json)发送 POST 请求AI 在生成用例或排查失败时可以实时调用这些工具查看页面状态而不是盲猜。3.3 SkillsAI 行为规范层Skills 这个概念在不同平台有不同叫法但本质是一致的给 AI 一套可读的指令文档约束它在特定任务里的行为模式。在 Web 自动化里Skills 可以是一个 Markdown 文件包含这些内容# Web 自动化测试规范 1. 所有元素定位优先使用># 创建虚拟环境 python -m venv .venv # 激活虚拟环境Windows 使用 .venv\Scripts\activate source .venv/bin/activate # 升级 pip python -m pip install --upgrade pip4.2 安装依赖# 测试框架相关 pip install pytest pytest-html pytest-xdist # 浏览器自动化相关 pip install selenium playwright # MCP 服务端相关 pip install mcp[cli] # HTTP 请求相关 pip install requests安装完成后初始化 Playwright 浏览器playwright install chromium这个步骤会下载 Chromium 内核国内网络环境下可能需要稍等片刻。如果下载失败可以换用系统已有的 Chromefrom playwright.sync_api import sync_playwright with sync_playwright() as p: browser p.chromium.launch(channelchrome)channelchrome会直接调用系统安装的 Chrome省去下载 Chromium 的时间。4.3 验证安装创建tests/test_smoke.pydef test_import(): import pytest import mcp import playwright print(依赖导入正常)运行pytest tests/test_smoke.py -v看到PASSED就说明环境没问题。5. 从零搭建一个 MCP Server 示例MCP Server 是整个链路里最关键的部分。AI 能不能“看见”页面、能不能操作浏览器全靠它。下面给出一个最简 MCP Server用 Python 实现提供几个 Web 自动化常用的工具。新建mcp_server.pyimport json from mcp.server.fastmcp import FastMCP # 创建 MCP Server 实例 mcp FastMCP(web-auto-server) mcp.tool() def get_page_info(url: str) - str: 获取网页的基本信息包括标题和状态码。 import requests response requests.get(url, timeout10) title 未找到 # 简化提取 title 标签 import re match re.search(rtitle(.*?)/title, response.text, re.S) if match: title match.group(1).strip() return json.dumps({ url: url, status_code: response.status_code, title: title }, ensure_asciiFalse) mcp.tool() def check_element_exists(url: str, selector: str) - str: 检查页面中是否存在指定的 CSS 选择器。 from playwright.sync_api import sync_playwright with sync_playwright() as p: browser p.chromium.launch(channelchrome, headlessTrue) page browser.new_page() page.goto(url, wait_untilnetworkidle, timeout15000) count page.locator(selector).count() browser.close() return json.dumps({ selector: selector, count: count, exists: count 0 }, ensure_asciiFalse) mcp.tool() def take_screenshot(url: str, save_path: str screenshot.png) - str: 对指定页面截图保存。 from playwright.sync_api import sync_playwright with sync_playwright() as p: browser p.chromium.launch(channelchrome, headlessTrue) page browser.new_page(viewport{width: 1280, height: 800}) page.goto(url, wait_untilload, timeout15000) page.screenshot(pathsave_path, full_pageTrue) browser.close() return f截图已保存到 {save_path} if __name__ __main__: mcp.run(transportstdio)启动 MCP Serverpython mcp_server.py默认走 stdio 传输适合本地接入 Claude Desktop、Cursor 这类客户端。如果希望走 HTTP 方式暴露服务可以用python mcp_server.py --transport http或者修改启动部分if __name__ __main__: mcp.run(transporthttp, host127.0.0.1, port8000)HTTP 模式下AI 客户端通过http://127.0.0.1:8000/mcp访问工具服务。这种方式适合把 MCP Server 部署在单独的机器上多个客户端共享同一套工具能力。5.1 在 AI 客户端里配置 MCP Server以常见 AI 编程工具为例配置 MCP Server 时需要指定启动命令。在 Claude Desktop 的配置文件中添加{ mcpServers: { web-auto: { command: python, args: [/path/to/mcp_server.py] } } }在 Cursor 里通过 Settings - MCP 添加 Server命令同样是python /path/to/mcp_server.py配置完成后AI 对话里就能发现get_page_info、check_element_exists、take_screenshot这几个工具。你让 AI“检查一下某个页面的登录按钮是否存在”它就会自动调用check_element_exists工具而不是凭训练数据猜。6. Pytest 工程结构设计与 fixture 封装MCP Server 解决了 AI 的工具能力问题接下来要解决的是用例组织问题。一个清晰的 Pytest 工程结构能让 AI 生成用例时更有章法也让后续执行、定位、维护都更省心。推荐的结构如下web_auto_project/ ├── mcp_server.py # MCP Server 入口 ├── skills/ │ ├── web_test_rules.md # AI 生成用例时的行为规范 │ └── login_test_strategy.md ├── tests/ │ ├── conftest.py # fixture 定义 │ ├── test_login.py │ ├── test_order_flow.py │ └── test_regression.py ├── pages/ │ ├── base_page.py │ └── login_page.py ├── data/ │ ├── test_accounts.json │ └── test_data.yaml ├── reports/ │ └── 测试报告输出目录 └── pytest.ini6.1 pytest.ini 基础配置[pytest] testpaths tests markers smoke: 冒烟测试 regression: 回归测试 slow: 慢速测试 addopts -v -s --disable-warnings6.2 conftest.py 核心 fixturefixture 是 Pytest 最实用的功能之一。在 AI 自动化场景里fixture 负责提供浏览器实例、测试数据、登录状态等公共能力。import json import pytest from playwright.sync_api import sync_playwright pytest.fixture(scopesession) def browser_context(): 全局浏览器实例测试结束后自动关闭。 with sync_playwright() as p: browser p.chromium.launch( channelchrome, headlessTrue ) context browser.new_context( viewport{width: 1440, height: 900}, ignore_https_errorsTrue ) yield context browser.close() pytest.fixture() def page(browser_context): 每个用例独立的页面对象。 context browser_context page context.new_page() yield page page.close() pytest.fixture() def test_accounts(): 读取测试账号避免在代码里硬编码。 with open(data/test_accounts.json, r, encodingutf-8) as f: return json.load(f)这里的关键设计是browser_context是 session 级 fixture所有用例共用一个浏览器实例速度更快。page是 function 级 fixture每个用例有自己的 page互不干扰。测试账号从 JSON 文件读取方便不同环境切换数据。6.3 一个完整的登录测试用例import pytest from pages.login_page import LoginPage pytest.mark.smoke def test_login_success(page, test_accounts): login_page LoginPage(page) account test_accounts[valid_user] login_page.goto() login_page.login(account[username], account[password]) assert login_page.is_login_success() is True assert login_page.get_welcome_text() 欢迎回来 pytest.mark.smoke def test_login_wrong_password(page, test_accounts): login_page LoginPage(page) account test_accounts[invalid_user] login_page.goto() login_page.login(account[username], account[wrong_password]) assert login_page.is_login_success() is False assert 用户名或密码错误 in login_page.get_error_message()正向反向用例都覆盖到断言清晰这是 AI 生成用例时必须遵守的规范。7. 用 Skills 约束 AI 生成高质量用例很多人在 AI 编程工具里遇到过一个问题让 AI 写测试用例它确实写了但写出来的用例风格松散、定位方式混乱、断言不够明确。原因在于你没有给 AI 一套“测试规范”。Skills 就是解决这个问题的。它的本质是一份给 AI 看的规范文档AI 在生成代码前先读这份文档再按文档要求输出。7.1 编写 Skills 文档在skills/web_test_rules.md中写# Web 测试用例生成规范 ## 目标 生成稳定、可维护、可读性强的 Web 自动化测试用例。 ## 元素定位优先级 1.># 执行全部用例 pytest # 只执行冒烟测试 pytest -m smoke # 按文件执行 pytest tests/test_login.py tests/test_order_flow.py # 并行执行 pytest -n 4-n 4表示 4 个进程并行跑来自 pytest-xdist 插件。注意 Playwright 用例并行时每个进程需要有独立的浏览器实例。8.2 失败自动重试安装 pytest-rerunfailurespip install pytest-rerunfailures执行时加入参数pytest --reruns 2 --reruns-delay 2这条命令表示用例失败后等待 2 秒重试最多重试 2 次。对于 Web 自动化里偶发的网络波动、元素加载超时重试是提升稳定性的有效手段——但前提是重试次数不能掩盖真实 bug建议只在冒烟和回归套件里重试关键用例不重试。8.3 批量任务调度脚本工程化落地时可以用一个 Python 脚本统一调度import subprocess import datetime def run_test_suite(suite_name: str, markers: str None): 执行指定的测试套件并生成报告。 timestamp datetime.datetime.now().strftime(%Y%m%d_%H%M%S) report_name freports/{suite_name}_{timestamp}.html cmd [ pytest, tests/, -m, markers, --html, report_name, --self-contained-html, --reruns, 2, --reruns-delay, 2 ] result subprocess.run(cmd, capture_outputTrue, textTrue) return { suite: suite_name, report: report_name, returncode: result.returncode, passed: result.stdout.count(PASSED), failed: result.stdout.count(FAILED), duration: timestamp } if __name__ __main__: smoke_result run_test_suite(smoke, markerssmoke) print(smoke_result) if smoke_result[returncode] 0: regression_result run_test_suite(regression, markersregression) print(regression_result)这样冒烟通过后才跑回归回归失败时能看到完整报告后续接到 CI 或者定时任务里也很方便。8.4 MCP 接口对接如果希望外部系统调用 MCP Server 提供的工具可以通过 HTTP 方式暴露。启动transporthttp后接口地址为POST http://127.0.0.1:8000/mcp请求体按 MCP 协议格式构造{ jsonrpc: 2.0, id: 1, method: tools/call, params: { name: get_page_info, arguments: { url: https://example.com } } }Python 侧调用示例import requests url http://127.0.0.1:8000/mcp payload { jsonrpc: 2.0, id: 1, method: tools/call, params: { name: get_page_info, arguments: { url: https://example.com } } } response requests.post(url, jsonpayload, timeout30) print(response.json())实际接口路径可能因 MCP SDK 版本而略有差异以本机启动后的日志为准。9. 性能观察与资源占用这套技术栈不涉及 GPU 推理资源占用主要在三个方向浏览器进程、Python 进程、AI 模型调用时的网络请求。9.1 怎么观察资源占用浏览器进程Playwright 启动的 Chromium 每个页面大约占用 200-500MB 内存页面数量越多内存越大。建议 headless 模式下运行回归并关掉不用的标签页。Python 进程MCP Server 本身的 CPU 占用很低真正耗资源的是 Playwright 操作页面时的解析和渲染。并行执行用例时-n 4会同时开 4 个浏览器实例内存会明显上升。AI 模型调用每次让 AI 生成或分析用例都会消耗模型 API 的 token。建议批量任务里不要频繁调用大模型先把用例数据准备好再一次性交给 AI 分析结果。9.2 降低资源占用的建议# 在 conftest.py 中限制浏览器并发 pytest.fixture(scopesession) def browser_context(): with sync_playwright() as p: browser p.chromium.launch( channelchrome, headlessTrue, args[--disable-gpu, --disable-dev-shm-usage] ) context browser.new_context() yield context browser.close()--disable-gpu在 headless 模式下可以降低 GPU 进程占用--disable-dev-shm-usage在 Docker 或低内存环境里能避免共享内存不足。9.3 延迟与稳定性观察AI 自动化测试的延迟通常来自两个环节AI 生成/修改用例时的模型响应时间一般是 5 到 30 秒看模型速度和输入长度。浏览器执行操作时的页面加载时间取决于被测系统本身的响应速度。建议在批量任务里不要做“AI 实时修改用例”的操作。更稳妥的做法是先批量执行收集失败用例再让 AI 分析失败原因并生成修复建议人工确认后更新代码。10. 常见问题与排查方法问题现象可能原因排查方式解决方案MCP Server 启动报错Python 环境缺少 mcp 依赖运行pip install mcp[cli]按依赖清单逐项安装AI 客户端发现不了工具MCP Server 未启动或配置路径错误检查客户端日志确认 Server 状态重新配置 command 和 argsPlaywright 启动浏览器失败Chromium 未安装或版本不匹配运行playwright install chromium换用channelchrome用例执行时元素定位失败选择器过期或页面未加载完成用 MCP 工具获取当前 DOM更新选择器增加显式等待Pytest 收集不到用例文件名不是test_*.py或函数名不符运行pytest --collect-only按规范重命名文件和函数并行执行时浏览器冲突多个进程共用了同一浏览器 profile检查 fixture 作用域每个进程独立实例或用--browserchromium参数MCP HTTP 接口 404传输方式不是 HTTP 或端口不对查看启动日志确认端口使用--transport http启动AI 生成的用例风格不统一没有配置 Skills 文档检查 AI 是否读取了 skills 文件在提示词中强制指定先读取规范文档10.1 MCP 工具不返回结果时如果 AI 调用 MCP 工具后没有返回结果先做三步排查单独运行python mcp_server.py确认服务能正常启动。在 AI 客户端里查看 MCP Server 日志看是否有报错堆栈。直接调用一个最简单的工具比如get_page_info确认网络请求能通。大多数 MCP 不返回的问题都不是协议问题而是 Server 启动失败或工具内部异常没被捕获。在工具函数外层加try-except会友好得多mcp.tool() def safe_get_page_info(url: str) - str: 获取网页信息异常时返回友好提示。 try: import requests response requests.get(url, timeout10) return response.text[:500] except Exception as e: return f获取失败: {str(e)}10.2 AI 生成用例质量不稳定时同样的提示词AI 每次生成的代码都可能不同。要稳定就得把约束从“对话里临时说”改成“文件里长期约束”。做法是把测试规范固定在skills/目录下的 Markdown 文件里。每个生成请求的提示词都包含“先读取 skills 目录下的规范文件”。生成后立刻用pytest --collect-only和flake8做静态校验不合格就让 AI 修改。这套流程执行下来AI 生成用例的质量会稳定在一个可接受的水平虽然达不到高级测试工程师的手写水准但作为用例初稿和回归验证已经完全够用。11. 最佳实践与合规边界11.1 工程化建议第一先小后大。第一次跑通时只测一个登录用例验证 MCP Server、AI 生成、Pytest 执行、报告输出这条链路是通的再逐步扩展到主流程。第二目录分开。模型文件、测试数据、测试用例、报告输出要分目录管理避免 AI 在生成用例时把数据和代码混在一起。第三AI 不直接改生产代码。AI 生成的用例先放分支跑通后 Review 再合入主干。批量任务里 AI 的修改要留痕方便回溯。第四接口服务限制访问。MCP Server 如果走 HTTP 方式暴露建议只绑定127.0.0.1不要直接暴露到公网。需要远程访问时加一层认证或放在内网。第五失败重试要有上限。重试机制只用于跳过偶发波动不能掩盖系统性的功能回归。建议重试上限为 2 次且每次重试后保存现场截图。11.2 合规与安全边界AI 做 Web 自动化测试必须严格遵守几条边界只测有授权的系统。被测站点必须是你有测试权限的环境不能对他人站点进行未经许可的自动化访问更不能拿这套能力去爬取他人网站的数据。测试数据要脱敏。测试账号、密码、个人信息等数据用测试环境专用数据禁止使用真实用户数据。不碰验证码绕过、权限绕过等内容。自动化测试只做功能验证不开发绕过安全机制的能力。涉及第三方接口的数据要确认授权。MCP 工具里如果接了内部接口要确认该接口允许测试环境调用。AI 生成内容要人工复核。AI 生成的用例代码在合入前必须做代码走查确认没有越权操作、没有硬编码敏感信息。11.3 通过 Skills 落实合规要求合规约束也可以写进 Skills 文档里## 合规要求 - 被测环境地址必须来自配置文件禁止硬编码公网地址。 - 测试数据只使用 data/ 目录下的测试专用数据。 - 禁止编写任何绕过登录验证、验证码、权限控制的代码。 - AI 生成的用例必须经过人工 Review 后才能合入主干。这样 AI 在生成用例时就会自动规避不合规的写法而不是靠人工事后排查。12. 总结与下一步Pytest Skills MCP 这套组合的价值核心在于把 AI 从“代码生成器”变成了“能自己看现场、自己调整策略的测试执行体”。MCP 让 AI 有了操作浏览器的能力Skills 让 AI 生成的用例有稳定的风格和质量基线Pytest 让整个过程可重复、可报告、可批量。如果你准备上手建议按这个顺序验证先跑通 MCP Server确认 AI 客户端能发现并调用工具。再跑通一条 Pytest 用例确认浏览器自动化链路正常。写一份 Skills 文档约束 AI 生成用例的规范。让 AI 生成一个登录用例并执行验证全链路闭环。再加批量任务和失败重试落到回归场景。最先要验证的不是多复杂的功能而是“AI 生成用例 - Pytest 执行 - 失败时 AI 能调用 MCP 工具看现场 - AI 给出修复建议”这条闭环。这条链路通了后面扩展页面、扩展业务场景都只是工作量问题不存在技术障碍。最容易踩的坑有三个MCP Server 配置路径不对导致工具发现不了、Playwright 浏览器下载失败导致用例无法执行、AI 生成用例时没有读 Skills 导致风格混乱。这三个坑在文章里都给了对应解法遇到时直接查表即可。后面可以继续扩展的方向包括把 MCP Server 接到内部接口平台自动生成接口级冒烟用例用 CI 定时任务结合 Teams/钉钉通知实现无人值守回归或者把 Skills 按业务线拆细比如交易链路一套规范、营销活动一套规范让 AI 在对应领域里更专业。