本文最后更新于 2026年7月12日 晚上
作者: LWMS2 开发团队日期: 2026-04-06版本: v1.0适用对象: 前端开发者、测试工程师、全栈开发者
📖 目录
什么是 MCP Playwright MCP (Model Context Protocol) 是一种标准化的协议,允许 AI 助手与外部工具进行交互。Playwright 是微软开发的现代化浏览器自动化工具。
MCP Playwright 将两者结合,让 AI 助手能够:
与传统调试方式的对比 特性 手动调试 传统自动化脚本 MCP Playwright
启动速度 慢(手动操作) 中等 快(AI 驱动)
灵活性 高 低(需预编写) 高(动态生成)
学习成本 低 高 中
问题定位 依赖经验 依赖脚本质量 AI 智能分析
文档化 手动记录 部分自动 完全自动化
为什么选择 MCP Playwright 1. 智能化调试 AI 可以:
自动分析错误信息
智能选择最优的选择器
动态调整调试策略
自动生成调试报告
2. 完整的上下文感知 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 1 2 3 4 5 6 - 当前页面 DOM 结构 - 控制台日志(console.log, error, warning) - 网络请求和响应 - 页面截图和视频 - localStorage/sessionStorage 状态
3. 高效的迭代循环 1 2 3
发现问题 → AI 分析 → 生成修复方案 → 自动验证 → 输出报告 ↓ ↑ └──────────── 快速迭代,无需手动干预 ──────────┘
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 每次调试都会生成: - 📸 关键步骤截图 - 📹 完整操作视频 - 📄 详细的 JSON 日志 - 📝 结构化的问题报告 --- ```bash 1 2 3 4 5 6 7 pip install playwright playwright install chromium playwright install chrome playwright install msedge
2. 配置 MCP 服务器 在项目根目录创建 .lingma/mcps/playwright/ 目录,确保包含以下文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 1 2 3 4 5 6 7 8 ├── SERVER_METADATA.json # 服务器元数据 └── tools/ ├── browser_navigate.json ├── browser_click.json ├── browser_type.json ├── browser_snapshot.json ├── browser_take_screenshot.json └── ... (其他工具定义)
3. 验证安装 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 1 2 3 4 5 6 7 8 from playwright.sync_api import sync_playwrightwith sync_playwright() as p: browser = p.chromium.launch(headless=True ) page = browser.new_page() page.goto("https://example.com" ) print (page.title()) browser.close()
预期输出:Example Domain
核心功能详解 1. 浏览器导航 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 1 2 3 4 5 6 7 8 page.goto("http://localhost:5173/login" ) page.wait_for_load_state("networkidle" ) page.wait_for_selector("#username" , timeout=5000 )
最佳实践:
2. 元素定位与交互 多种选择器策略 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 1 2 3 4 5 6 7 8 9 10 11 12 13 page.locator('input[name="username"]' ) page.locator('.btn-primary' ) page.locator('#login-form > button[type="submit"]' ) page.locator('//button[contains(text(), "登录")]' ) page.locator('text=忘记密码' ) page.locator('input[name="username"], input[id="username"]' ).first
常见交互操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 page.fill('input[name="username"]' , 'admin' ) page.fill('input[name="password"]' , 'admin123' ) page.click('button[type="submit"]' ) page.select_option('select#warehouse' , 'WH001' ) page.set_input_files('input[type="file"]' , 'test.pdf' ) page.press('input#search' , 'Enter' ) page.press('body' , 'Control+A' )
3. 截图与录制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 1 2 3 4 5 6 7 8 9 10 11 12 page.screenshot(path='full_page.png' , full_page=True ) element = page.locator('#error-message' ) element.screenshot(path='error_element.png' ) context = browser.new_context( record_video_dir='videos/' , record_video_size={'width' : 1920 , 'height' : 1080 } )
截图命名规范:
1 2 3 4 5 6 7 8 9 10 11 12 13 1 2 3 4 5 debug_01_initial.png # 初始状态 debug_02_filled.png # 填写完成后 debug_03_after_click.png # 点击后 debug_04_error.png # 错误状态 debug_05_success.png # 成功状态
4. 日志捕获 控制台日志 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 1 2 3 4 5 6 7 8 9 10 11 console_messages = []def handle_console (msg ): console_messages.append({ 'type' : msg.type , 'text' : msg.text, 'location' : f"{msg.location.get('url' , '' )} :{msg.location.get('lineNumber' , '' )} " }) print (f"[Console {msg.type } ] {msg.text} " ) page.on("console" , handle_console)
网络请求监控 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 1 2 3 4 5 6 7 8 9 10 11 12 network_requests = []def handle_response (response ): network_requests.append({ 'url' : response.url, 'method' : response.request.method, 'status' : response.status, 'headers' : dict (response.headers), 'timestamp' : time.time() }) page.on("response" , handle_response)
5. JavaScript 执行 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 auth_data = page.evaluate("""() => { return { token: localStorage.getItem('token'), user: localStorage.getItem('user') }; }""" ) page.evaluate("""() => { document.body.style.backgroundColor = 'red'; }""" ) result = page.evaluate("""(arg1, arg2) => { return window.myFunction(arg1, arg2); }""" , "value1" , "value2" )
实战案例:登录问题排查 问题背景 用户报告无法登录系统,提示”登录失败,请检查用户名和密码”。
调试流程 Step 1: 创建调试脚本 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 """ 登录功能调试脚本 - debug_login.py """ from playwright.sync_api import sync_playwrightimport timeimport jsondef debug_login (): print ("=" *80 ) print ("🔍 开始调试登录功能" ) print ("=" *80 ) with sync_playwright() as p: browser = p.chromium.launch( headless=False , channel="chrome" , slow_mo=500 , args=[ '--no-sandbox' , '--disable-infobars' , '--disable-extensions' ] ) context = browser.new_context(viewport={'width' : 1920 , 'height' : 1080 }) page = context.new_page() console_messages = [] network_requests = [] page.on("console" , lambda msg: console_messages.append({ 'type' : msg.type , 'text' : msg.text })) page.on("response" , lambda res: network_requests.append({ 'url' : res.url, 'status' : res.status, 'method' : res.request.method })) print ("\n→ 导航到登录页面..." ) page.goto('http://localhost:5173/login' ) page.wait_for_load_state('networkidle' ) time.sleep(2 ) page.screenshot(path='debug_01_initial.png' , full_page=True ) print ("📸 已保存初始状态截图" ) print ("\n🔍 检查页面元素..." ) username_input = page.locator('input[name="username"]' ).first password_input = page.locator('input[name="password"]' ).first login_button = page.locator('button[type="submit"]' ).first assert username_input.count() > 0 , "❌ 未找到用户名输入框" assert password_input.count() > 0 , "❌ 未找到密码输入框" assert login_button.count() > 0 , "❌ 未找到登录按钮" print ("✓ 所有必需元素都存在" ) print ("\n→ 填写用户名: admin" ) username_input.fill('admin' ) print ("→ 填写密码: admin123" ) password_input.fill('admin123' ) time.sleep(1 ) page.screenshot(path='debug_02_filled.png' , full_page=True ) print ("\n→ 点击登录按钮..." ) login_button.click() time.sleep(3 ) page.screenshot(path='debug_03_after_click.png' , full_page=True ) current_url = page.url print (f"\n→ 当前 URL: {current_url} " ) if '/login' in current_url: print ("⚠️ 仍在登录页面,登录可能失败" ) error_element = page.locator('.bg-red-50' ).first if error_element.count() > 0 : error_text = error_element.inner_text() print (f"❌ 错误提示: {error_text} " ) page.screenshot(path='debug_04_error.png' , full_page=True ) else : print ("✅ 已跳转到首页,登录成功" ) page.screenshot(path='debug_05_success.png' , full_page=True ) debug_info = { 'console_messages' : console_messages[-20 :], 'network_requests' : [r for r in network_requests if 'api' in r['url' ]], 'current_url' : current_url, 'timestamp' : time.time() } with open ('debug_login_info.json' , 'w' , encoding='utf-8' ) as f: json.dump(debug_info, f, ensure_ascii=False , indent=2 ) print ("\n📄 调试信息已保存到 debug_login_info.json" ) print ("\n按 Enter 关闭浏览器..." ) input () browser.close()if __name__ == '__main__' : debug_login()
Step 2: 运行调试脚本 1 2 3 4 5 6 7 1 2cd backend python scripts/debug_login.py
Step 3: 分析输出 发现的关键信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 1 2 3 4 5 6 [Console error ] Access to XMLHttpRequest at 'http://localhost:8000/api/v1/auth/login' from origin 'http://localhost:5173' has been blocked by CORS policy [Console error ] Failed to load resource: net::ERR_FAILED ❌ 错误提示: 登录失败,请检查用户名和密码
后端日志显示:
1 2 3 4 5 6 7 8 9 1 2 3 ValueError: password cannot be longer than 72 bytesFile "app/core/security.py" , line 26 , in verify_password return pwd_context.verify (plain_password, hashed_password)
Step 4: 定位根本原因 通过综合分析:
❌ CORS 错误 - 实际是表象,请求被后端拒绝
❌ bcrypt 兼容性问题 - passlib 与新版 bcrypt 不兼容
✅ 根本原因 - 密码验证逻辑崩溃导致返回错误响应
Step 5: 实施修复 修改 backend/app/core/security.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from passlib.context import CryptContext pwd_context = CryptContext(schemes=["bcrypt" ], deprecated="auto" )def verify_password (plain_password: str , hashed_password: str ) -> bool : return pwd_context.verify(plain_password, hashed_password)import bcryptdef verify_password (plain_password: str , hashed_password: str ) -> bool : try : return bcrypt.checkpw( plain_password.encode('utf-8' ), hashed_password.encode('utf-8' ) ) except Exception: return False
重置密码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 1 2 3 4 5 6 7 8 9 10 11 12 13 14 import bcryptfrom app.db.session import SessionLocalfrom app.models.user import User db = SessionLocal() user = db.query(User).filter (User.username == "admin" ).first()if user: password = b'admin123' salt = bcrypt.gensalt() hashed = bcrypt.hashpw(password, salt) user.password_hash = hashed.decode('utf-8' ) db.commit() print ("✅ 密码更新成功" )
Step 6: 验证修复 重新运行调试脚本,确认:
✅ 无 CORS 错误
✅ 无控制台错误
✅ 成功跳转到首页
✅ localStorage 中有 token
最佳实践 1. 浏览器配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 browser = p.chromium.launch( headless=False , channel="chrome" , slow_mo=500 , args=[ '--no-sandbox' , '--disable-infobars' , '--disable-extensions' , '--disable-popup-blocking' ] ) browser = p.chromium.launch( headless=True , slow_mo=0 , args=[] )
2. 元素定位策略 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 page.locator('input[name="username"]' ) page.locator('#login-button' ) page.locator('[data-testid="submit-btn"]' ) page.locator('.btn.btn-primary' ) page.locator('text=登录' ) page.locator('//div[@class="form"]/input[1]' ) page.locator('div > div > div > button' ) page.locator(':nth-child(3)' )
3. 等待策略 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 page.wait_for_load_state('networkidle' ) page.wait_for_selector('#element' , state='visible' ) page.wait_for_selector('#button' , state='enabled' )with page.expect_response("**/api/login" ) as response_info: page.click('#login-btn' ) response = response_info.valueassert response.status == 200 time.sleep(5 )
4. 错误处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 try : page.goto('http://localhost:5173/login' , timeout=10000 )except TimeoutError: print ("❌ 页面加载超时" ) page.screenshot(path='error_timeout.png' ) raise except Exception as e: print (f"❌ 未知错误: {e} " ) page.screenshot(path='error_unknown.png' ) raise username_input = page.locator('input[name="username"]' )assert username_input.count() > 0 , "用户名输入框不存在" assert username_input.is_visible(), "用户名输入框不可见" assert username_input.is_enabled(), "用户名输入框被禁用"
5. 资源清理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 def run_test (): browser = None try : with sync_playwright() as p: browser = p.chromium.launch() page = browser.new_page() finally : if browser: browser.close() print ("✅ 浏览器已关闭" )with sync_playwright() as p: browser = p.chromium.launch() try : finally : browser.close()
常见问题与解决方案 Q1: 浏览器启动失败 症状:
1 2 3 4 5 1 playwright._impl._errors.Error: BrowserType.launch: Executable doesn't exist
解决:
1 2 3 4 5 6 7 8 9 1 2 3 playwright install chromium playwright install chrome
Q2: 元素找不到 症状:
1 2 3 4 5 1 TimeoutError: locator.click: Timeout 30000 ms exceeded
解决:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 print (page.content()) page.screenshot(path='debug_current.png' ) element = page.locator('input[name="username"], input#username, .username-input' ).first page.wait_for_selector('input[name="username"]' , timeout=10000 ) frame = page.frame_locator('iframe#content-frame' ) frame.locator('input[name="username"]' ).fill('admin' )
Q3: CORS 跨域错误 症状:
1 2 3 4 5 1 Access to XMLHttpRequest has been blocked by CORS policy
解决:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 BACKEND_CORS_ORIGINS = [ "http://localhost:5173" , "http://127.0.0.1:5173" ] context = browser.new_context( bypass_csp=True , ignore_https_errors=True )import requests response = requests.get('http://localhost:8000/health' )print (response.status_code)
Q4: 异步操作不同步 症状:
1 2 3 4 5 1 Element is not attached to the DOM
解决:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 1 2 3 4 5 6 7 8 9 10 element = page.locator('#dynamic-element' ) element.wait_for(state='attached' ) element.wait_for(state='visible' ) element.click()from playwright.sync_api import expect expect(element).to_be_visible(timeout=5000 ) element.click()
Q5: 内存泄漏 症状:
1
浏览器占用内存持续增长
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 * * 解决:* * ```python1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 for test_case in test_cases: context = browser.new_context() page = context.new_page() try: run_test(page) finally: context.close() from concurrent.futures import ThreadPoolExecutorwith ThreadPoolExecutor(max_workers= 3 ) as executor: futures = [executor.submit(run_test, url) for url in urls] for future in futures: future.result()
高级技巧 1. 自定义认证状态 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 context = browser.new_context() page = context.new_page() page.goto('http://localhost:5173/login' ) page.fill('input[name="username"]' , 'admin' ) page.fill('input[name="password"]' , 'admin123' ) page.click('button[type="submit"]' ) page.wait_for_url('**/' ) context.storage_state(path='auth_state.json' ) new_context = browser.new_context(storage_state='auth_state.json' ) new_page = new_context.new_page() new_page.goto('http://localhost:5173/dashboard' )
2. 网络拦截与 Mock 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 page.route('**/api/users' , lambda route: route.fulfill( status=200 , content_type='application/json' , body=json.dumps([ {'id' :1 , 'name' : 'Mock User' } ]) )) page.route('**/analytics/**' , lambda route: route.abort()) requests_log = [] page.route('**/*' , lambda route: ( requests_log.append({ 'url' : route.request.url, 'method' : route.request.method }), route.continue_() ))
3. 视觉回归测试 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from PIL import Imageimport numpy as npdef compare_screenshots (baseline_path, current_path, threshold=0.01 ): """比较两张截图的差异""" baseline = np.array(Image.open (baseline_path)) current = np.array(Image.open (current_path)) if baseline.shape != current.shape: return False , "尺寸不匹配" diff = np.mean(np.abs (baseline.astype(float ) - current.astype(float ))) / 255 is_similar = diff < threshold return is_similar, f"差异度: {diff:.4 f} " page.screenshot(path='current.png' ) is_similar, message = compare_screenshots('baseline.png' , 'current.png' )print (message)
4. 性能监控 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 performance_metrics = page.evaluate("""() => { const timing = performance.getEntriesByType('navigation')[0]; return { dnsLookup: timing.domainLookupEnd - timing.domainLookupStart, tcpConnection: timing.connectEnd - timing.connectStart, ttfb: timing.responseStart - timing.requestStart, domContentLoaded: timing.domContentLoadedEventEnd - timing.navigationStart, pageLoad: timing.loadEventEnd - timing.navigationStart }; }""" )print (f"DNS 查询: {performance_metrics['dnsLookup' ]} ms" )print (f"TCP 连接: {performance_metrics['tcpConnection' ]} ms" )print (f"首字节: {performance_metrics['ttfb' ]} ms" )print (f"DOM 就绪: {performance_metrics['domContentLoaded' ]} ms" )print (f"页面加载: {performance_metrics['pageLoad' ]} ms" )
5. 并行测试 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 def pytest_configure (config ): config.option.numprocesses = 4 def test_login_with_different_users (page, username, password ): page.goto('http://localhost:5173/login' ) page.fill('input[name="username"]' , username) page.fill('input[name="password"]' , password) page.click('button[type="submit"]' ) expect(page).to_have_url('**/dashboard' ) pytest tests/ -n 4 --browser chromium
总结与展望 核心优势回顾
未来发展方向
🤖 更智能的元素识别 - 基于 AI 的智能选择器生成
📊 可视化调试面板 - 实时展示调试过程和结果
🔗 CI/CD 集成 - 自动化回归测试和质量门禁
🌍 跨浏览器测试 - 同时测试 Chrome、Firefox、Safari
📱 移动端支持 - 响应式设计和移动端适配测试
行动建议
附录 A. 常用命令速查 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 pip install playwright playwright install pip install --upgrade playwright playwright install --force playwright --version playwright codegen http://localhost:5173 playwright inspector
B. 参考资源
C. 工具链推荐 用途 工具 说明
浏览器自动化 Playwright 本文主角
API 测试 Postman / Insomnia 接口调试
性能分析 Lighthouse 页面性能
视觉测试 Percy / Chromatic 视觉回归
测试框架 pytest Python 测试
CI/CD GitHub Actions 自动化流水线
文档维护者: LWMS2 开发团队最后更新: 2026-04-06反馈与建议: 欢迎提交 Issue 或 PR
💡 提示: 本文档会随着项目发展和实践经验不断更新,建议定期查看最新版本。