# 第20-25页:测试验收篇 > 章节:03 测试验收 > 核心问题:AI 辅助下的测试策略是什么?如何让 AI 操作浏览器?如何生成 E2E 测试? --- ## 第20页:分隔页(03 测试验收) ### 原文内容 章节分隔页,标志进入"测试验收"部分 ### 研究要点 - 这是4个核心问题中的第3个 - 测试验收是保证 AI 生成代码质量的关键 - 核心目标:自动化测试,减少人工验证 --- ## 第21页:AI 辅助下的测试策略是什么? ### 原文内容 **测试策略全景图**: | 测试类型 | 测试对象 | 工具(含 AI) | 负责人(人/Agent) | |---------|---------|--------------|-------------------| | **Lint 代码扫描** | 代码风格、代码逻辑和片段 | TS Lint/SonarQube | 程序 | | **Code Review** | 代码逻辑、架构设计、规范(复杂度/安全/可维护性) | GitHub Copilot、Claude Code、CodeRabbit、GitHub PR | 开发者 + AI Review Agent | | **单元测试** | 函数/类行为、边界条件、异常处理 | JUnit、pytest、Jest + AI 生成测试代码(Copilot/Claude) | 开发者 + Test Generation Agent | | **API 测试** | 接口契约(Schema)、输入输出、鉴权、错误码、业务规则 | Karate(主)、RestAssured(补充复杂逻辑) + AI 生成测试用例/数据 | QA + 后端开发 + API Test Agent | | **E2E 测试** | 用户关键路径(登录/下单/支付)、跨系统流程、前后端联动 | Playwright + AI 生成脚本/自愈(selector 修复、步骤补全) | QA + 自动化测试工程师 + E2E Agent | ### 深入解读 **测试金字塔在 AI 时代的演变** 传统测试金字塔: ``` E2E 测试(少量) / \ / 集成测试 \ / (中等数量) \ / \ / 单元测试 \ / (大量) \ /_________________________\ ``` AI 时代的测试策略: - **AI 生成大量单元测试**:覆盖边界条件 - **AI 生成 API 测试**:基于 OpenAPI 规范 - **AI 操作浏览器做 E2E 测试**:模拟真实用户 - **人工专注于 Code Review**:审查 AI 的审查结果 **Lint 代码扫描** **工具选择**: - **前端**:ESLint、Prettier - **后端**:SonarQube、Checkstyle - **多语言**:MegaLinter **AI 的作用**: - 自动修复格式问题 - 检测潜在的 bug - 提供修复建议 **示例**: ```bash # 前端 npm run lint --fix # 后端(Java) mvn checkstyle:check # SonarQube sonar-scanner ``` **Code Review** **AI 辅助工具**: - **GitHub Copilot Code Review**:自动审查 PR - **Claude Code**:可以审查整个代码库 - **CodeRabbit**:专业的 AI 代码审查工具 - **GitHub PR + AI**:集成 AI 审查功能 **AI Review 的优势**: - 速度快:几分钟内完成审查 - 覆盖广:可以检查多个维度(安全、性能、规范) - 一致性:不会遗漏 **AI Review 的局限**: - 无法理解业务逻辑 - 可能误报 - 需要人工复核 **示例**: ```markdown # AI Review 报告 ## 安全问题 - [高] 第 42 行:SQL 拼接存在注入风险 - [中] 第 78 行:敏感信息未加密 ## 性能问题 - [中] 第 156 行:循环内查询数据库,建议批量查询 ## 代码规范 - [低] 第 23 行:变量命名不符合规范 ``` **单元测试** **AI 生成测试的优势**: - 快速生成大量测试用例 - 覆盖边界条件 - 生成 Mock 数据 **工具**: - **Java**:JUnit 5 + Mockito - **Python**:pytest - **前端**:Jest、Vitest **示例(AI 生成的单元测试)**: ```java class UserServiceTest { @Mock private UserRepository userRepository; @InjectMocks private UserService userService; @Test void should_create_user_successfully() { // Given CreateUserRequest request = new CreateUserRequest(); request.setUsername("testuser"); request.setEmail("test@example.com"); request.setPassword("Password123"); when(userRepository.existsByUsername("testuser")).thenReturn(false); when(userRepository.save(any(User.class))).thenAnswer(invocation -> invocation.getArgument(0)); // When UserResponse response = userService.create(request); // Then assertNotNull(response); assertEquals("testuser", response.getUsername()); verify(userRepository).save(any(User.class)); } @Test void should_throw_exception_when_username_exists() { // Given CreateUserRequest request = new CreateUserRequest(); request.setUsername("existinguser"); when(userRepository.existsByUsername("existinguser")).thenReturn(true); // When & Then assertThrows(BusinessException.class, () -> { userService.create(request); }); } @Test void should_validate_email_format() { // Given CreateUserRequest request = new CreateUserRequest(); request.setEmail("invalid-email"); // When & Then assertThrows(ValidationException.class, () -> { userService.create(request); }); } } ``` **API 测试** **工具选择**: - **Karate**:BDD 风格的 API 测试框架 - **RestAssured**:Java API 测试库 - **Postman**:可视化 API 测试 **AI 的作用**: - 根据 OpenAPI 规范生成测试用例 - 生成测试数据 - 自动生成断言 **示例(Karate 测试)**: ```gherkin Feature: User API Scenario: Create user successfully Given url 'http://localhost:8080/api/v1/users' And request { username: 'testuser', email: 'test@example.com', password: 'Password123' } When method POST Then status 201 And match response.data.username == 'testuser' And match response.data.email == 'test@example.com' Scenario: Create user with duplicate username Given url 'http://localhost:8080/api/v1/users' And request { username: 'existinguser', email: 'test2@example.com', password: 'Password123' } When method POST Then status 409 And match response.message == '用户名已存在' ``` **E2E 测试** **工具选择**: - **Playwright**:最流行的 E2E 测试框架 - **Cypress**:前端 E2E 测试 - **Selenium**:老牌 E2E 测试工具 **AI 的作用**: - 生成测试脚本 - 自愈(selector 修复) - 步骤补全 **示例(Playwright 测试)**: ```typescript import { test, expect } from '@playwright/test'; test('用户注册流程', async ({ page }) => { // 访问注册页面 await page.goto('http://localhost:3000/register'); // 填写表单 await page.fill('[name="username"]', 'testuser'); await page.fill('[name="email"]', 'test@example.com'); await page.fill('[name="password"]', 'Password123'); await page.fill('[name="confirmPassword"]', 'Password123'); // 提交 await page.click('button[type="submit"]'); // 验证跳转 await expect(page).toHaveURL(/\/login/); // 验证提示信息 await expect(page.locator('.alert-success')).toContainText('注册成功,请查收验证邮件'); }); ``` ### 实践建议 - **分层测试**:单元测试 → API 测试 → E2E 测试 - **AI 生成测试**:利用 AI 快速生成大量测试用例 - **人工审查**:AI 生成的测试需要人工审查 - **持续集成**:所有测试集成到 CI/CD 流程 --- ## 第22页:AI 如何操作浏览器? ### 原文内容 **四种方案对比**: | 维度 | Playwright MCP | Chrome DevTools MCP | Browser Use | Computer Use | |------|---------------|---------------------|-------------|--------------| | **原理** | 通过 Playwright 访问浏览器 Accessibility Tree,获取结构化 DOM 快照,AI 基于节点操作 | 通过 Chrome DevTools Protocol 直接与浏览器引擎通信,暴露 CDP 全部能力(DOM/网络/控制台) | Python 框架 + Playwright,AI 自主决策循环;支持 DOM 和截图双模 | AI 截取屏幕截图 → 视觉识别元素 → 输出坐标/按键操作 → 沙箱执行,OS 级通用 | | **抽象层** | Accessibility Tree 结构化 DOM 快照 | CDP Protocol DevTools 协议原生 | DOM + 截图 视觉 + 结构化混合 | 截图 + 坐标 OS 级视觉理解 | | **后端引擎** | Playwright | Puppeteer + CDP | Playwright + Python | Anthropic API + 沙箱 | | **速度** | 快 ~0.9s/步 | 中 ~1.2s/步 | 中 ~1.5s/步 | 慢 0.8-2s/步 | | **Token 消耗** | 高 截图+结构全传 | 中 按需取数据 | 极低 CLI 模式 ~75 tok/步 | 高 截图编码开销大 | | **JS 重页面** | 中 — DOM 可读 | 中 — CDP 可取 | 中 — 视觉兜底 | 高 — 视觉理解 | | **跨应用操作** | 仅浏览器 | 仅浏览器 | 仅浏览器 | 全桌面 | ### 深入解读 **方案1:Playwright MCP** **原理**: - 使用 Playwright 打开浏览器 - 获取 Accessibility Tree(可访问性树) - AI 基于树节点进行操作 **优势**: - 速度快(~0.9s/步) - 结构化信息丰富 - 支持所有现代浏览器 **劣势**: - Token 消耗高(需要传递完整 DOM 结构) - 仅限浏览器操作 **适用场景**: - E2E 测试 - 表单自动填充 - 数据抓取 **示例**: ```typescript import { chromium } from 'playwright'; const browser = await chromium.launch(); const page = await browser.newPage(); await page.goto('https://example.com'); // AI 获取 Accessibility Tree const snapshot = await page.accessibility.snapshot(); console.log(snapshot); // AI 基于树节点操作 await page.click('button[name="submit"]'); await page.fill('input[name="username"]', 'testuser'); ``` **方案2:Chrome DevTools MCP** **原理**: - 使用 Chrome DevTools Protocol (CDP) - 直接与浏览器引擎通信 - 暴露完整的浏览器能力 **优势**: - 功能最强大(可以访问所有 CDP 能力) - 可以监控网络请求、控制台日志 - 性能分析 **劣势**: - 学习曲线陡峭 - 仅限 Chrome/Chromium **适用场景**: - 性能分析 - 网络请求监控 - 复杂的浏览器自动化 **示例**: ```javascript const CDP = require('chrome-remote-interface'); (async () => { const client = await CDP(); const { Page, Runtime } = client; // 导航到页面 await Page.navigate({ url: 'https://example.com' }); // 执行 JavaScript const result = await Runtime.evaluate({ expression: 'document.title' }); console.log(result.result.value); // 监控网络请求 Page.requestWillBeSent(({ request }) => { console.log('Request:', request.url); }); await client.close(); })(); ``` **方案3:Browser Use** **原理**: - Python 框架 - 基于 Playwright - AI 自主决策循环 - 支持 DOM 和截图双模式 **优势**: - Token 消耗极低(CLI 模式 ~75 tok/步) - AI 自主决策 - 支持视觉理解 **劣势**: - 仅限浏览器操作 - 依赖 Python 生态 **适用场景**: - 复杂的网页自动化 - 需要视觉理解的场景 - Token 成本敏感的项目 **示例**: ```python from browser_use import Agent agent = Agent( task="Go to Reddit, search for 'r/LocalLLaMA' and click on the first post", llm=llm ) result = await agent.run() print(result) ``` **方案4:Computer Use(Anthropic)** **原理**: - AI 截取屏幕截图 - 视觉识别元素 - 输出坐标/按键操作 - 沙箱执行 **优势**: - 可以操作任何应用(不限于浏览器) - 可以处理复杂的视觉场景 - OS 级通用 **劣势**: - 速度慢(0.8-2s/步) - Token 消耗高 - 依赖视觉识别准确性 **适用场景**: - 桌面应用自动化 - 需要跨应用操作 - 复杂的视觉场景 **示例**: ```python from anthropic import Anthropic client = Anthropic() response = client.messages.create( model="claude-3-5-sonnet-20241022", max_tokens=1024, tools=[{ "type": "computer_20241022", "name": "computer", "display_width_px": 1024, "display_height_px": 768, }], messages=[{ "role": "user", "content": "Open Chrome and go to google.com" }] ) ``` ### 方案选择指南 | 场景 | 推荐方案 | 理由 | |------|---------|------| | E2E 测试 | Playwright MCP | 速度快,结构化信息丰富 | | 性能分析 | Chrome DevTools MCP | 可以监控网络和性能 | | 网页自动化 | Browser Use | Token 消耗低,AI 自主决策 | | 桌面应用 | Computer Use | 可以操作任何应用 | | Token 成本敏感 | Browser Use | 极低 Token 消耗 | | 复杂视觉场景 | Computer Use | 视觉理解能力强 | ### 实践建议 - **E2E 测试首选 Playwright MCP**:速度快,生态成熟 - **Token 成本敏感选 Browser Use**:极低 Token 消耗 - **需要跨应用选 Computer Use**:OS 级通用 - **性能分析选 Chrome DevTools MCP**:功能最强大 --- ## 第23页:如何用测试用例生成 E2E 测试? ### 原文内容 **三步法**: **Step 1: Browser Use 探索** - AI 自主遍历页面,记录操作轨迹、选择器、页面状态,输出 Playwright 脚本雏形 - 消耗 Token **Step 2: Playwright E2E 固化** - 将探索结果转为 Playwright 测试脚本,加入 CI,零 Token 可重复运行 **Step 3: 截图视觉兜底** - Playwright 覆盖不到的视觉场景(布局、动效、图表),重回截图判断 ### 深入解读 **为什么需要三步法?** - **Browser Use 探索**:AI 自主探索,快速生成测试脚本雏形 - **Playwright 固化**:将探索结果固化,零 Token 可重复运行 - **截图视觉兜底**:处理 Playwright 无法覆盖的视觉场景 **Step 1: Browser Use 探索** **流程**: 1. AI 打开浏览器 2. AI 自主探索页面 3. 记录操作轨迹(点击、输入、导航) 4. 记录选择器(元素定位) 5. 生成 Playwright 脚本雏形 **示例**: ```python # 原始指令 agent = Agent( task=""" 1. 打开 /login 页面 2. 输入用户名 test@example.com 3. 输入密码 123456 4. 点击登录按钮 5. 验证跳转到 /dashboard 6. 验证页面显示 "Welcome" """, llm=llm ) # AI 探索后生成的 Playwright 脚本雏形 """ test('login flow', async ({ page }) => { await page.goto('/login'); await page.fill('[name="email"]', 'test@example.com'); await page.fill('[name="password"]', '123456'); await page.click('button[type="submit"]'); await expect(page).toHaveURL('/dashboard'); await expect(page.locator('text=Welcome')).toBeVisible(); }); """ ``` **Token 消耗**: - 探索阶段:消耗大量 Token - 每次探索:可能需要多次尝试 - 成本:每次探索约 $0.5-2 **Step 2: Playwright E2E 固化** **流程**: 1. 将 AI 生成的脚本雏形人工审查 2. 优化选择器(使用更稳定的定位方式) 3. 添加断言 4. 加入 CI/CD 5. 零 Token 可重复运行 **优化后的 Playwright 脚本**: ```typescript import { test, expect } from '@playwright/test'; test.describe('登录流程', () => { test('成功登录并跳转到仪表盘', async ({ page }) => { // 访问登录页 await page.goto('/login'); // 使用更稳定的选择器 await page.getByLabel('邮箱').fill('test@example.com'); await page.getByLabel('密码').fill('123456'); await page.getByRole('button', { name: '登录' }).click(); // 验证跳转 await expect(page).toHaveURL(/\/dashboard/); // 验证页面内容 await expect(page.getByText('Welcome')).toBeVisible(); }); test('登录失败 - 密码错误', async ({ page }) => { await page.goto('/login'); await page.getByLabel('邮箱').fill('test@example.com'); await page.getByLabel('密码').fill('wrongpassword'); await page.getByRole('button', { name: '登录' }).click(); // 验证错误提示 await expect(page.getByText('密码错误')).toBeVisible(); }); }); ``` **成本**: - 固化阶段:人工审查,无 Token 消耗 - 运行阶段:零 Token - CI/CD:每次运行约 10-30 秒 **Step 3: 截图视觉兜底** **场景**: - 视觉回归测试(布局偏移、样式异常) - 动效/动画测试(加载态、过渡动画) - Canvas/WebGL 测试(图表渲染、游戏画面) **流程**: 1. Playwright 截图 2. 调用 AI 视觉模型分析 3. 判断是否符合预期 **示例**: ```typescript test('视觉回归测试', async ({ page }) => { await page.goto('/dashboard'); // 截图 const screenshot = await page.screenshot(); // 调用 AI 视觉模型分析 const result = await analyzeScreenshot(screenshot, { expected: '仪表盘页面,包含侧边栏、顶部导航、主要内容区域', checks: [ '侧边栏在左侧', '顶部导航在顶部', '主要内容区域在中间', '没有明显的布局异常' ] }); expect(result.passed).toBe(true); }); // AI 视觉分析函数 async function analyzeScreenshot(screenshot: Buffer, options: any) { const response = await openai.chat.completions.create({ model: 'gpt-4-vision-preview', messages: [{ role: 'user', content: [ { type: 'text', text: `请分析这张截图:${options.expected}` }, { type: 'image_url', image_url: { url: `data:image/png;base64,${screenshot.toString('base64')}` } } ] }] }); // 解析 AI 的响应 return parseAIResponse(response); } ``` **成本**: - 截图阶段:无 Token 消耗 - AI 分析:每次约 $0.01-0.05 - 适合关键页面的视觉验证 ### 实践建议 - **先用 Browser Use 探索**:快速生成测试脚本雏形 - **人工审查后固化**:优化选择器,加入 CI/CD - **关键页面截图验证**:视觉回归测试 - **成本优化**:探索阶段消耗 Token,固化后零 Token --- ## 第24页:Playwright E2E 示例 ### 原文内容 **完整的 Playwright E2E 测试示例**: ```typescript import { test, expect, request } from '@playwright/test'; test.describe('User Login Flow', () => { test('login success and dashboard visible', async ({ page }) => { // 1. 打开登录页 await page.goto('https://example.com/login'); // 2. 填写表单 await page.getByPlaceholder('Email').fill('test@example.com'); await page.getByPlaceholder('Password').fill('123456'); // 3. 提交 await page.getByRole('button', { name: 'Login' }).click(); // 4. 等待跳转 & 断言 await expect(page).toHaveURL(/dashboard/); await expect(page.getByText('Welcome')).toBeVisible(); }); test('login via API + set session (更稳定)', async ({ page, request }) => { // 通过 API 登录,设置 session const response = await request.post('https://example.com/api/login', { data: { email: 'test@example.com', password: '123456' } }); expect(response.ok()).toBeTruthy(); // 获取 session cookie const cookies = await response.headers()['set-cookie']; // 设置 cookie 到浏览器 await page.context().addCookies([{ name: 'session', value: cookies, domain: 'example.com', path: '/' }]); // 访问仪表盘 await page.goto('https://example.com/dashboard'); await expect(page.getByText('Welcome')).toBeVisible(); }); }); ``` ### 深入解读 **两种登录方式的对比** **方式1:UI 登录** - 优点:测试真实的用户流程 - 缺点:慢,依赖 UI 元素 - 适用:测试登录页面本身 **方式2:API 登录** - 优点:快,稳定,不依赖 UI - 缺点:无法测试登录页面 - 适用:需要登录状态的其他页面测试 **选择器最佳实践** **推荐的选择器(从优到差)**: ```typescript // 1. 使用 data-testid(最稳定) await page.getByTestId('submit-button').click(); // 2. 使用角色和名称(推荐) await page.getByRole('button', { name: 'Login' }).click(); // 3. 使用标签文本(推荐) await page.getByLabel('Email').fill('test@example.com'); // 4. 使用占位符(可用) await page.getByPlaceholder('Enter your email').fill('test@example.com'); // 5. 使用文本(可用) await page.getByText('Welcome').toBeVisible(); // 6. 使用 CSS 选择器(不推荐,容易变) await page.locator('.btn-primary').click(); // 7. 使用 XPath(不推荐,容易变) await page.locator('//button[@type="submit"]').click(); ``` **等待策略** **自动等待**: ```typescript // Playwright 自动等待元素可见、可操作 await page.getByRole('button', { name: 'Login' }).click(); ``` **显式等待**: ```typescript // 等待 URL 变化 await expect(page).toHaveURL(/dashboard/); // 等待元素可见 await expect(page.getByText('Welcome')).toBeVisible(); // 等待元素隐藏 await expect(page.getByText('Loading')).toBeHidden(); // 等待网络请求完成 await page.waitForLoadState('networkidle'); ``` **测试数据管理** **方式1:硬编码** ```typescript await page.getByLabel('Email').fill('test@example.com'); ``` **方式2:使用 Fixture** ```typescript test.use({ storageState: 'auth.json' }); test('已登录用户访问仪表盘', async ({ page }) => { await page.goto('/dashboard'); // 已经是登录状态 }); ``` **方式3:使用 API 准备数据** ```typescript test.beforeEach(async ({ request }) => { // 通过 API 创建测试用户 await request.post('/api/users', { data: { email: 'test@example.com', password: '123456' } }); }); test.afterEach(async ({ request }) => { // 清理测试数据 await request.delete('/api/users/test@example.com'); }); ``` **并行测试** **配置**: ```typescript // playwright.config.ts export default defineConfig({ workers: 4, // 4 个并行 worker projects: [ { name: 'chromium', use: { browserName: 'chromium' } }, { name: 'firefox', use: { browserName: 'firefox' } }, { name: 'webkit', use: { browserName: 'webkit' } }, ], }); ``` ### 实践建议 - **优先使用稳定的选择器**:data-testid、角色、标签 - **使用 API 登录**:提高测试速度 - **测试数据管理**:使用 Fixture 或 API 准备数据 - **并行测试**:提高测试效率 - **CI/CD 集成**:每次提交自动运行测试 --- ## 第25页:超级 Loop(E2E Loop) ### 原文内容 **超级 Loop 流程图**: ``` 开始进入循环 ↓ [Chat] 进入 Plan 模式 ↓ 装载需求规格/界面原型,给出指令进行 Plan ↓ [Chat] 符合要求,退出 Plan 模式,开始执行? ↓ 是 [模型] 读取 Plan,按照 Loop 规则进行实现 ↓ [模型] 满足 Loop 准出要求? ↓ 否 重复多次 / 白天持续很久 / 夜间 ↓ 是 [模型] 退出循环 ↓ (给予 ByPass 权限) ``` **核心思想**: - 通过 E2E 测试纳入更大的 Loop 范围 **Loop 规则(放到 AGENTS.md 作为开发流程要求)**: 1. **Plan 等待确认**:在 docs/plans 下根据模版创建 Plan 2. **编写测试用例**:在 docs/test-cases 下编写 E2E 测试用例 3. **编写 API 测试**:根据 Plan 实现 API 测试,运行成功并断言失败 4. **编写实现**:编写单元测试 + 实现代码,通过编译 5. **运行 API 测试**:运行测试,并修复失败的测试和其他错误 6. **浏览器调试**:使用 Playwright 调试,确认前端视觉效果和交互功能正常 7. **编写 E2E 测试**:编写 Playwright E2E 测试,覆盖本轮需求的测试场景 ### 深入解读 **什么是超级 Loop?** - 定义:在核心 Loop 基础上,增加 E2E 测试验证 - 目标:确保前后端联动的正确性 - 特点:覆盖完整的用户流程 **超级 Loop vs 核心 Loop** | 维度 | 核心 Loop | 超级 Loop | |------|----------|----------| | 测试范围 | API 测试 | API 测试 + E2E 测试 | | 验证层次 | 后端逻辑 | 前后端联动 | | 复杂度 | 中等 | 高 | | 运行时间 | 几分钟 | 几十分钟到几小时 | | Token 消耗 | 中等 | 高 | | 适用场景 | 后端开发 | 全栈开发 | **超级 Loop 的7个步骤** **步骤1:Plan 等待确认** ```markdown # Plan: 实现用户注册功能(含前端) ## 目标 实现用户注册功能,包括后端 API 和前端页面 ## 任务列表 - [ ] 1. 实现 POST /api/v1/users API - [ ] 2. 实现注册页面 /register - [ ] 3. 实现表单验证 - [ ] 4. 实现注册成功跳转 - [ ] 5. 编写 API 测试 - [ ] 6. 编写 E2E 测试 ## 验收标准 - 所有 API 测试通过 - 所有 E2E 测试通过 - 前端页面视觉效果符合设计稿 ``` **步骤2:编写测试用例** ```markdown # docs/test-cases/register.md ## 测试用例 ### TC-001: 成功注册 1. 访问 /register 页面 2. 输入用户名 testuser 3. 输入邮箱 test@example.com 4. 输入密码 Password123 5. 确认密码 Password123 6. 点击注册按钮 7. 验证跳转到 /login 8. 验证提示"注册成功" ### TC-002: 用户名已存在 1. 访问 /register 页面 2. 输入已存在的用户名 existinguser 3. 输入邮箱 test2@example.com 4. 输入密码 Password123 5. 确认密码 Password123 6. 点击注册按钮 7. 验证提示"用户名已存在" ### TC-003: 密码不匹配 1. 访问 /register 页面 2. 输入用户名 testuser2 3. 输入邮箱 test3@example.com 4. 输入密码 Password123 5. 确认密码 Password456 6. 点击注册按钮 7. 验证提示"密码不匹配" ``` **步骤3:编写 API 测试** ```java @Test void testRegister() { CreateUserRequest request = new CreateUserRequest(); request.setUsername("testuser"); request.setEmail("test@example.com"); request.setPassword("Password123"); Response response = userClient.create(request); assertEquals(201, response.getCode()); assertNotNull(response.getData().getId()); } ``` **步骤4:编写实现** ```java // 后端实现 @PostMapping("/users") public Response createUser(@Valid @RequestBody CreateUserRequest request) { return Response.success(userAppService.create(request)); } // 前端实现(React) function RegisterPage() { const [form] = Form.useForm(); const handleSubmit = async (values) => { await axios.post('/api/v1/users', values); message.success('注册成功'); navigate('/login'); }; return (
); } ``` **步骤5:运行 API 测试** ```bash mvn test -Dtest=UserApiTest # 预期:所有测试通过 ``` **步骤6:浏览器调试** ```typescript // 使用 Playwright 调试前端 test('注册页面视觉效果', async ({ page }) => { await page.goto('/register'); // 截图验证视觉效果 const screenshot = await page.screenshot(); // 调用 AI 视觉模型验证 // 验证表单验证 await page.fill('[name="username"]', 'testuser'); await page.fill('[name="email"]', 'invalid-email'); await page.click('button[type="submit"]'); await expect(page.getByText('请输入有效的邮箱')).toBeVisible(); }); ``` **步骤7:编写 E2E 测试** ```typescript test('完整的注册流程', async ({ page }) => { // 访问注册页面 await page.goto('/register'); // 填写表单 await page.getByLabel('用户名').fill('testuser'); await page.getByLabel('邮箱').fill('test@example.com'); await page.getByLabel('密码').fill('Password123'); await page.getByLabel('确认密码').fill('Password123'); // 提交 await page.getByRole('button', { name: '注册' }).click(); // 验证跳转 await expect(page).toHaveURL(/\/login/); // 验证提示 await expect(page.getByText('注册成功')).toBeVisible(); }); ``` ### 实践建议 - **先写测试用例**:明确验收标准 - **API 测试先通过**:确保后端逻辑正确 - **浏览器调试**:验证前端视觉效果 - **E2E 测试覆盖**:确保前后端联动正确 - **超级 Loop 成本高**:仅在必要时使用(全栈开发) --- ## 本章小结 **测试验收的核心要点**: 1. AI 辅助测试策略:Lint → Code Review → 单元测试 → API 测试 → E2E 测试 2. AI 操作浏览器:Playwright MCP、Chrome DevTools MCP、Browser Use、Computer Use 3. 生成 E2E 测试:Browser Use 探索 → Playwright 固化 → 截图视觉兜底 4. Playwright E2E 示例:稳定的选择器、API 登录、测试数据管理 5. 超级 Loop:在核心 Loop 基础上增加 E2E 测试验证 **关键工具**: - Playwright、Browser Use - Karate、RestAssured - SonarQube、ESLint - CodeRabbit、GitHub Copilot **最佳实践**: - 分层测试:单元测试 → API 测试 → E2E 测试 - 先用 Browser Use 探索,再固化到 Playwright - 使用稳定的选择器: