Files
chill_notes/AI工程/团队级AI_Coding简明手册_逐页研究/03_测试验收_第20-25页.md
2026-06-22 11:30:51 +08:00

28 KiB
Executable File
Raw Blame History

第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
  • 提供修复建议

示例

# 前端
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 的局限

  • 无法理解业务逻辑
  • 可能误报
  • 需要人工复核

示例

# AI Review 报告

## 安全问题
- [高] 第 42 行SQL 拼接存在注入风险
- [中] 第 78 行:敏感信息未加密

## 性能问题
- [中] 第 156 行:循环内查询数据库,建议批量查询

## 代码规范
- [低] 第 23 行:变量命名不符合规范

单元测试

AI 生成测试的优势

  • 快速生成大量测试用例
  • 覆盖边界条件
  • 生成 Mock 数据

工具

  • JavaJUnit 5 + Mockito
  • Pythonpytest
  • 前端Jest、Vitest

示例AI 生成的单元测试)

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 测试

工具选择

  • KarateBDD 风格的 API 测试框架
  • RestAssuredJava API 测试库
  • Postman:可视化 API 测试

AI 的作用

  • 根据 OpenAPI 规范生成测试用例
  • 生成测试数据
  • 自动生成断言

示例Karate 测试)

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 测试)

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 框架 + PlaywrightAI 自主决策循环;支持 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 可取 中 — 视觉兜底 高 — 视觉理解
跨应用操作 仅浏览器 仅浏览器 仅浏览器 全桌面

深入解读

方案1Playwright MCP

原理

  • 使用 Playwright 打开浏览器
  • 获取 Accessibility Tree可访问性树
  • AI 基于树节点进行操作

优势

  • 速度快(~0.9s/步)
  • 结构化信息丰富
  • 支持所有现代浏览器

劣势

  • Token 消耗高(需要传递完整 DOM 结构)
  • 仅限浏览器操作

适用场景

  • E2E 测试
  • 表单自动填充
  • 数据抓取

示例

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');

方案2Chrome DevTools MCP

原理

  • 使用 Chrome DevTools Protocol (CDP)
  • 直接与浏览器引擎通信
  • 暴露完整的浏览器能力

优势

  • 功能最强大(可以访问所有 CDP 能力)
  • 可以监控网络请求、控制台日志
  • 性能分析

劣势

  • 学习曲线陡峭
  • 仅限 Chrome/Chromium

适用场景

  • 性能分析
  • 网络请求监控
  • 复杂的浏览器自动化

示例

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();
})();

方案3Browser Use

原理

  • Python 框架
  • 基于 Playwright
  • AI 自主决策循环
  • 支持 DOM 和截图双模式

优势

  • Token 消耗极低CLI 模式 ~75 tok/步)
  • AI 自主决策
  • 支持视觉理解

劣势

  • 仅限浏览器操作
  • 依赖 Python 生态

适用场景

  • 复杂的网页自动化
  • 需要视觉理解的场景
  • Token 成本敏感的项目

示例

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)

方案4Computer UseAnthropic

原理

  • AI 截取屏幕截图
  • 视觉识别元素
  • 输出坐标/按键操作
  • 沙箱执行

优势

  • 可以操作任何应用(不限于浏览器)
  • 可以处理复杂的视觉场景
  • OS 级通用

劣势

  • 速度慢0.8-2s/步)
  • Token 消耗高
  • 依赖视觉识别准确性

适用场景

  • 桌面应用自动化
  • 需要跨应用操作
  • 复杂的视觉场景

示例

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 UseOS 级通用
  • 性能分析选 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 脚本雏形

示例

# 原始指令
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 脚本

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. 判断是否符合预期

示例

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 测试示例

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();
  });
});

深入解读

两种登录方式的对比

方式1UI 登录

  • 优点:测试真实的用户流程
  • 缺点:慢,依赖 UI 元素
  • 适用:测试登录页面本身

方式2API 登录

  • 优点:快,稳定,不依赖 UI
  • 缺点:无法测试登录页面
  • 适用:需要登录状态的其他页面测试

选择器最佳实践

推荐的选择器(从优到差)

// 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();

等待策略

自动等待

// Playwright 自动等待元素可见、可操作
await page.getByRole('button', { name: 'Login' }).click();

显式等待

// 等待 URL 变化
await expect(page).toHaveURL(/dashboard/);

// 等待元素可见
await expect(page.getByText('Welcome')).toBeVisible();

// 等待元素隐藏
await expect(page.getByText('Loading')).toBeHidden();

// 等待网络请求完成
await page.waitForLoadState('networkidle');

测试数据管理

方式1硬编码

await page.getByLabel('Email').fill('test@example.com');

方式2使用 Fixture

test.use({
  storageState: 'auth.json'
});

test('已登录用户访问仪表盘', async ({ page }) => {
  await page.goto('/dashboard');
  // 已经是登录状态
});

方式3使用 API 准备数据

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');
});

并行测试

配置

// 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页超级 LoopE2E 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个步骤

步骤1Plan 等待确认

# Plan: 实现用户注册功能(含前端)

## 目标
实现用户注册功能,包括后端 API 和前端页面

## 任务列表
- [ ] 1. 实现 POST /api/v1/users API
- [ ] 2. 实现注册页面 /register
- [ ] 3. 实现表单验证
- [ ] 4. 实现注册成功跳转
- [ ] 5. 编写 API 测试
- [ ] 6. 编写 E2E 测试

## 验收标准
- 所有 API 测试通过
- 所有 E2E 测试通过
- 前端页面视觉效果符合设计稿

步骤2编写测试用例

# 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 测试

@Test
void testRegister() {
    CreateUserRequest request = new CreateUserRequest();
    request.setUsername("testuser");
    request.setEmail("test@example.com");
    request.setPassword("Password123");
    
    Response<UserResponse> response = userClient.create(request);
    
    assertEquals(201, response.getCode());
    assertNotNull(response.getData().getId());
}

步骤4编写实现

// 后端实现
@PostMapping("/users")
public Response<UserResponse> 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 (
    <Form form={form} onFinish={handleSubmit}>
      <Form.Item name="username" rules={[{ required: true }]}>
        <Input placeholder="用户名" />
      </Form.Item>
      <Form.Item name="email" rules={[{ required: true, type: 'email' }]}>
        <Input placeholder="邮箱" />
      </Form.Item>
      <Form.Item name="password" rules={[{ required: true, min: 8 }]}>
        <Input.Password placeholder="密码" />
      </Form.Item>
      <Button type="primary" htmlType="submit">注册</Button>
    </Form>
  );
}

步骤5运行 API 测试

mvn test -Dtest=UserApiTest
# 预期:所有测试通过

步骤6浏览器调试

// 使用 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 测试

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
  • 使用稳定的选择器: