智能体驱动的 E2E 测试:不是替代,而是新的一层

2026-06-11 16 预计阅读时间: 1 分钟
来源: slack.engineering AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:10 分钟

端到端测试一直有个矛盾——写得太死,跟不上产品变化;写得太活,又没法稳定复现。AI Agent 进入测试栈后,这个矛盾有了新的解法,但也带来了新的混淆:Agent 该替代传统测试,还是叠加在上面?一支团队跑了 200 多条 agentic E2E workflow,用 Playwright MCP、Playwright CLI 和 Agent 自动生成的 Playwright 测试脚本,在非生产数据的工作空间里反复验证,得出的结论很明确——Agent 是探索层,不是确定性层

传统 E2E 测试的确定性瓶颈

传统 Playwright 测试的核心思路是:定位元素 → 操作 → 断言结果。每一步都写死在代码里:

# 传统确定性测试——每一步都硬编码
from playwright.sync_api import sync_playwright

def test_login_flow():
    with sync_playwright() as p:
        browser = p.chromium.launch()
        page = browser.new_page()
        page.goto("https://app.example.com/login")
        page.fill("#email", "user@test.com")
        page.fill("#password", "secret123")
        page.click("button[type='submit']")
        assert page.url == "https://app.example.com/dashboard"
        browser.close()

这种测试的优点是稳定、可复现、CI 里跑一千次结果一致。缺点也明显:页面改一个 selector,几十条测试全挂;新功能上线,测试覆盖永远滞后;只能测"已知路径",测不了"用户可能乱点出来的意外路径"。

Agent 进入测试栈的三种形态

实验中出现了三种 Agent 参与 E2E 测试的方式,各有适用边界:

形态一:Playwright MCP——Agent 实时操控浏览器

Playwright MCP 把浏览器操作暴露为 Model Context Protocol 的工具调用。Agent 不写脚本,而是实时决策下一步操作:

// MCP 工具调用示例——Agent 通过工具协议直接操控浏览器
{
  "tool": "playwright_navigate",
  "arguments": { "url": "https://app.example.com/login" }
}
// Agent 观察页面后自主决定下一步
{
  "tool": "playwright_click",
  "arguments": { "selector": "button[data-testid='submit']" }
}

这种方式最灵活,Agent 可以根据页面实际状态动态调整路径。但每次运行结果可能不同——Agent 这次走了登录流程,下次可能先逛了一圈设置页。适合探索性测试,不适合 CI 门禁。

形态二:Playwright CLI——Agent 调用命令行工具

Agent 通过 shell 命令驱动 Playwright CLI,适合需要批量操作或与脚本组合的场景:

# Agent 生成并执行的 CLI 命令序列
npx playwright codegen --target=python https://app.example.com

# Agent 也可以直接运行已有测试并分析结果
npx playwright test --reporter=json tests/smoke/ 2>&1 | \
  jq '.suites[].tests[] | select(.status=="failed") | .name'

CLI 方式让 Agent 可以混合使用"生成脚本"和"执行脚本"两种能力,但本质上还是非确定性的——Agent 每次可能生成不同的命令序列。

形态三:Agent 生成 Playwright 测试代码

Agent 不直接跑测试,而是生成确定性测试脚本,再由传统流程执行:

# Agent 自动生成的 Playwright 测试——写完后人审、CI 跑
import re
from playwright.sync_api import Page, expect

def test_checkout_with_discount(page: Page):
    """Agent 生成:验证折扣码在结算流程中正确生效"""
    page.goto("/cart")
    # Agent 学会了用 data-testid 而非脆弱的 CSS selector
    page.click("[data-testid='proceed-to-checkout']")
    page.fill("[data-testid='discount-code']", "SAVE20")
    page.click("[data-testid='apply-discount']")
    # 断言折扣金额出现且数值合理
    discount_text = page.locator("[data-testid='discount-amount']").text_content()
    discount_value = float(re.search(r"\d+\.\d{2}", discount_text).group())
    assert discount_value > 0, "折扣金额应为正数"
    subtotal = float(page.locator("[data-testid='subtotal']").text_content().split("$")[1])
    assert discount_value <= subtotal * 0.3, "折扣不应超过小计的30%"

这是三种形态中唯一适合进 CI 的——生成的代码是确定性的,人审核后锁定版本,后续运行结果稳定可复现。

实践:搭建一个两层测试栈

把探索层和确定性层分开,是实验最重要的结论。下面是一个可以直接改造使用的项目结构:

# 项目目录结构
tests/
├── deterministic/          # 传统 + Agent 生成的确定性测试,进 CI   ├── login.spec.py
│   ├── checkout.spec.py    # Agent 生成,人审核后提交   └── smoke/
├── exploratory/            # Agent 实时探索,不进 CI,本地或定时跑   ├── agent_mcp_configs/  # MCP 工具配置   └── exploration_prompts/
└── shared/
    └── test_data.py        # 非生产测试数据工厂

确定性测试的 CI 配置:

# .github/workflows/e2e-deterministic.yml
name: E2E Deterministic Tests
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      - run: pip install playwright pytest
      - run: playwright install chromium
      - run: pytest tests/deterministic/ --timeout=60s
        env:
          TEST_ENV: staging  # 只用非生产数据

探索性测试的定时任务(不阻塞 CI):

# .github/workflows/e2e-exploratory.yml
name: E2E Exploratory Agent Scan
on:
  schedule:
    - cron: "0 6 * * 1-5"  # 工作日早上跑一次
jobs:
  explore:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run agentic exploration
        run: |
          npx @anthropic-ai/claude-code \
            --prompt "用 Playwright MCP 工具探索 staging 环境的完整用户流程,记录所有异常行为" \
            --allowedTools "playwright_navigate,playwright_click,playwright_fill,playwright_screenshot" \
            --maxTurns 50
        env:
          MCP_ENDPOINT: ${{ secrets.STAGING_MCP_URL }}
      - name: Parse agent findings
        run: python scripts/parse_exploration_report.py
      - name: Create issues for bugs found
        uses: actions/create-issue@v1
        with:
          title-prefix: "[Exploratory] "

关键设计决策:探索层发现问题后,不是直接修代码,而是把 bug 转成确定性测试再进 CI。这保证了修复验证是可复现的。

测试数据:非生产环境是底线

实验明确要求所有 agentic 测试都在非生产数据环境运行。Agent 的行为不可完全预测,让它操作生产数据库是高风险行为。实践中可以这样隔离:

# shared/test_data.py——非生产数据工厂
import faker

fake = faker.Faker()

def create_test_user(env="staging"):
    """只在 staging 创建测试用户,绝不碰生产"""
    return {
        "email": f"e2e_test_{fake.uuid4()}@test.example.com",
        "password": "TestPass!2024",
        "env": env,  # 确认环境标记
    }

# Agent 生成的测试必须引用这个工厂,不能硬编码真实用户

该把 Agent 放在哪一层?一个决策清单

根据 200 多条 workflow 的实验结果,可以这样决策:

场景 用什么 进 CI?
核心业务流程回归 传统 Playwright 或 Agent 生成的脚本 ✅ 是
新功能首次覆盖 Agent 生成脚本 → 人审核 → 提交 ✅ 审核后是
未知路径探索、边缘 case Playwright MCP 实时探索 ❌ 否
大范围冒烟扫描 Playwright CLI + Agent 批量执行 ❌ 否,结果转 issue
性能/并发压测 传统工具,Agent 不适合 ❌ Agent 延迟不可控

不要做的事:把 MCP 实时探索放进 CI pipeline 当门禁。Agent 这次跑通了,下次可能选了不同路径,CI 会变成随机通过/失败,团队很快就会忽略测试结果。

应该做的事:让 Agent 每天定时探索,发现的 bug 转成确定性测试,再进 CI。这样 Agent 的不确定性变成了发现能力,确定性测试保证了验证的可靠。

风险和边界

实验也暴露了几个实际问题:

  • Agent 幻觉定位器:Agent 有时会生成页面上不存在的 selector,MCP 模式下会反复尝试无效操作。解决方法是给 Agent 提供 data-testid 映射表作为上下文。
  • 成本不可忽视:200 条 workflow 的 token 消耗不小。探索层应该限制 maxTurns,避免 Agent 无限循环。
  • 结果归因困难:Agent 探索发现了一个 bug,但复现步骤可能不清晰。需要让 Agent 在探索时同步截图和 DOM 快照,方便后续写确定性测试时参考。

Agentic E2E 测试不是推翻旧体系,而是在确定性测试之上加一层"会思考的探索"。两层各司其职,确定性层守门,探索层找盲区——这才是 Agent 在测试栈里的正确位置。


相关推荐