Spec 驱动开发的核心是:先定义「做什么」,再推导「怎么做」,最后验证「做对了没」。当我们把这套理念应用到前端自动化测试领域,会发生什么?本文介绍一种基于 Kiro Power 的自动化测试 Spec 模式,通过「三步式」工作流,将测试需求转化为可直接运行的 Playwright 脚本。

一、什么是自动化测试的 Spec 模式

如果你用过 Kiro 的 Spec 驱动开发,一定熟悉这个流程:Requirements → Design → Tasks。先明确需求规格,再做技术设计,最后拆解为可执行的任务。AI 负责执行,人负责审查。

我们把同样的理念搬到了前端自动化测试:

Spec 驱动开发自动化测试 Spec 模式对应关系
Requirements(需求文档)test-cases.md(测试用例)先定义「测什么」
Design(技术设计)element-map.md(元素映射)基于真实探测确定「怎么测」
Tasks(实现任务)xxx.spec.ts(测试脚本)严格遵循规格生成代码

这不是简单的类比。两者共享同一套核心优势:

  • 需求先行,实现后置 — 先系统化生成测试用例,确保覆盖完备,再考虑技术实现
  • 规格即资产 — 中间产物不是临时文件,而是长期有效的、可审查可复用的测试资产
  • 阶段解耦 — 需求变了改用例,页面改了更新元素映射,脚本风格要调整就重新生成,互不影响
  • AI 执行,人审查 — AI 负责全部技术工作,人只需在每个阶段确认输出是否符合预期
  • 全链路可追溯 — 从原始需求到最终脚本,每一步都有文档记录,任何断言值都可追溯到真实接口响应

二、传统自动化测试的痛点

在展开方案细节之前,先看看传统做法为什么不够好:

痛点描述
选择器不稳定凭经验猜测 CSS 选择器,页面一改就失效
断言不可靠期望值靠人工推测,与实际接口响应不一致
维护成本高需求变更后需手动逐一修改脚本
知识断层测试用例、元素信息、脚本之间缺乏关联,难以追溯
微前端适配难wujie / qiankun 等微前端框架下,Shadow DOM 穿透选择器编写复杂

传统做法是:测试人员手动打开浏览器 DevTools → 逐个复制选择器 → 编写测试脚本 → 反复调试。一个中等复杂度的模块,往往需要数小时才能完成。

根本原因在于:传统方式是「代码驱动」的,直接从需求跳到脚本编写,中间缺少结构化的规格定义环节。而 Spec 模式通过引入文档化的中间层,让每一步都有据可依。

三、三步式 Spec 工作流

3.1 整体架构

在这里插入图片描述

3.2 关键设计决策

文档驱动,而非代码驱动:每个阶段的产出物都是结构化的 Markdown 文档。人类可读、可审查、可修改,阶段之间解耦,任何一步出错都可以单独重做。

选择器来源于真实探测,而非猜测:步骤二通过 Playwright 实际启动浏览器访问目标应用,探测并记录真实的页面元素选择器,同时记录备选选择器提高容错性。

接口响应优先的断言策略:前端 Toast 消息显示时间极短(2-5秒),自动化捕获不稳定。接口响应是确定性的、可靠的数据源,因此采用「接口响应拦截」作为第一优先级断言手段。

3.3 技术栈

组件技术选型说明
AI 编排引擎Kiro Power通过 Kiro Power 定义 AI Agent 的行为规范和技能编排
测试框架Playwright支持多浏览器、自动等待、网络拦截
脚本语言TypeScript类型安全,与 Playwright 原生集成
文档格式Markdown通用、可读、可版本控制

3.4 Kiro Power 技能编排

我们将工作流拆分为一个总控文件和三个技能文件:

在这里插入图片描述

总控文件始终加载,为 AI 提供全局上下文。步骤技能文件按需通过 # 引用激活,避免上下文污染。环境初始化技能在首次使用或依赖缺失时自动触发。

四、Demo 演示:以「配置管理系统」为例

我们以一个典型的企业级配置管理系统为例,演示完整的 Spec 工作流。该系统包含以下功能模块:

在这里插入图片描述

4.1 步骤一:需求分解(Requirements)

用户只需提供简单的需求描述:

登录功能:选择账号密码登录、输入用户名和密码
导航至配置页面
添加配置:选择供应商、填写 AppId、AppKey、BaseUrl
查询:输入 AppId 进行筛选
编辑:修改供应商、AppId、AppKey、BaseUrl
删除配置

AI 自动将需求拆分为 6 个功能模块,为每个模块生成标准化的测试用例文档。以登录模块为例,AI 生成了 8 个测试用例,覆盖 P0-P2 优先级:

用例编号场景优先级类型
TC-001正确账号密码登录P0正向
TC-002用户名为空登录P1反向
TC-003密码为空登录P1反向
TC-004错误密码登录P0反向
TC-005不存在的用户名登录P1反向
TC-006密码含特殊字符登录P2边界
TC-007超长用户名登录P2边界
TC-008连续多次错误密码登录P1异常

同时生成自动化流程文档(automation-flow.md),预留选择器字段待步骤二填充。这就是 Spec 模式中「先定义规格」的体现 — 在动手写任何代码之前,先把「测什么、怎么测」想清楚。

4.2 步骤二:页面探测(Design)

这是整个方案最核心的环节,也是 Spec 模式中「基于事实做设计」的体现。AI 生成 Playwright 探测脚本,实际启动浏览器访问目标应用,自动完成以下工作:

元素选择器识别:按优先级策略(data-testid > id > aria-label > name > placeholder > text > css)识别最优选择器,同时记录备选选择器。

// 探测脚本核心逻辑示例
async function probe() {
  const browser = await chromium.launch({ headless: false });
  const page = await browser.newPage();
  
  // 访问目标页面
  await page.goto(TARGET_URL, { waitUntil: 'networkidle' });
  
  // 自动识别页面元素
  const inputs = await page.evaluate(() => {
    const result: any[] = [];
    document.querySelectorAll('input').forEach(el => {
      const rect = el.getBoundingClientRect();
      if (rect.width > 0 && rect.height > 0) {
        result.push({
          id: el.id,
          name: (el as HTMLInputElement).name,
          type: (el as HTMLInputElement).type,
          placeholder: (el as HTMLInputElement).placeholder,
        });
      }
    });
    return result;
  });
  
  // 注册接口响应监听
  const responsePromise = page.waitForResponse(
    resp => resp.url().includes('/api/login') 
            && resp.request().method() === 'POST',
    { timeout: 15000 }
  );
  
  // 执行操作并捕获接口响应
  await page.click('button:has-text("登录")');
  const response = await responsePromise;
  const body = await response.json();
  
  // 记录接口响应用于后续断言
  console.log('接口响应:', JSON.stringify(body));
}

接口响应拦截:自动拦截关键接口的请求和响应,记录实际返回内容作为断言依据。

探测完成后,生成结构化的元素映射文档(element-map.md):

## 元素列表

### 元素 1: 账户密码登录切换标签
- 推荐选择器: `.rootAnt-tabs-tab-btn:has-text("账户密码登录")`
- 选择器类型: css+text
- 备选选择器: `text=账户密码登录`
- 特殊说明: 默认激活的是"域控登录"标签,需要先点击切换

### 元素 2: 用户名输入框
- 推荐选择器: `#username`
- 选择器类型: id
- 备选选择器: `input[placeholder="用户名:"]`
- 关联测试用例: TC-001, TC-002, TC-003

### 元素 3: 登录按钮
- 推荐选择器: `button:has-text("登 录")`
- 选择器类型: css+text
- 备选选择器: `.rootAnt-btn-primary.rootAnt-btn-lg`
- 特殊说明: 按钮文本有空格:"登 录"(Ant Design 特征)

## 接口响应记录

### 账号密码正常登录 (TC-001)
- 请求: POST /api/user/login
- 响应:
  {"code":"00000","msg":"success","success":true,"token":"eyJhbGciOiJIUzI1NiJ9..."}

### 密码错误登录失败 (TC-002)
- 请求: POST /api/user/login
- 响应:
  {"code":"500","msg":"用户名或者密码错误","success":false}

同时生成关键步骤的截图,便于人工审查。这些产出物就是 Spec 模式中的「设计文档」— 基于真实探测结果,而非凭空猜测。

4.3 步骤三:脚本生成(Tasks)

基于步骤一的测试用例和步骤二的元素映射,AI 生成可直接运行的 Playwright 测试脚本:

import { test, expect } from '@playwright/test';
import loginData from './fixtures/login.json';

test.describe('登录功能', () => {
  
  test.beforeEach(async ({ page }) => {
    await page.goto(loginData.baseUrl);
    // 切换到账号密码登录 Tab(默认激活的是"域控登录")
    await page.click('.rootAnt-tabs-tab-btn:has-text("账户密码登录")');
  });

  test('TC-001: 账号密码正常登录', async ({ page }) => {
    await page.fill('#username', loginData.validUser);
    await page.fill('#pwd', loginData.validPassword);
    
    // 监听登录接口响应
    const responsePromise = page.waitForResponse(
      resp => resp.url().includes('/api/user/login') 
              && resp.request().method() === 'POST'
    );
    
    // 注意:按钮文本有空格"登 录"(Ant Design 特征)
    await page.click('button:has-text("登 录")');
    
    const response = await responsePromise;
    const body = await response.json();
    
    // 断言值来源于 element-map.md 中记录的实际接口响应
    expect(body.code).toBe('00000');
    expect(body.success).toBe(true);
    expect(body.token).toBeTruthy();
  });

  test('TC-002: 密码错误登录失败', async ({ page }) => {
    await page.fill('#username', loginData.validUser);
    await page.fill('#pwd', 'wrong_password');
    
    const responsePromise = page.waitForResponse(
      resp => resp.url().includes('/api/user/login')
              && resp.request().method() === 'POST'
    );
    
    await page.click('button:has-text("登 录")');
    
    const response = await responsePromise;
    const body = await response.json();
    
    // 断言值来源于实际拦截的接口响应
    expect(body.code).toBe('500');
    expect(body.msg).toBe('用户名或者密码错误');
    expect(body.success).toBe(false);
  });

  test('TC-003: 用户名为空登录', async ({ page }) => {
    await page.fill('#pwd', loginData.validPassword);
    await page.click('button:has-text("登 录")');
    
    // 前端表单校验,不发送接口请求
    const error = page.locator('.rootAnt-form-item-explain-error');
    await expect(error).toContainText('用户名是必填项');
  });
});

每一个选择器都可以追溯到 element-map.md,每一个断言值都可以追溯到实际拦截的接口响应。这就是 Spec 模式的力量 — 实现严格遵循规格,不偏离设计意图。

4.4 微前端场景下的探测

在 供应商配置管理模块中,页面架构为 wujie 微前端 + NocoBase 低代码平台。探测脚本自动识别页面架构并选择最优策略:

页面架构探测结果:
- 主应用: Ant Design Pro(CSS 前缀: rootAnt-)
- 功能页面: wujie 微前端 + NocoBase(CSS 前缀: ant-)
- iframe 直接访问 URL: /nocobase/admin/xxx?embed=true
- 直接访问验证: 通过(916 个元素,页面完整加载)
- 表单引擎: NocoBase 内置表单
- 数据集合名: app_config

方案内置了三种页面架构的自动检测和适配策略:

策略 A:iframe 直接访问模式(本 Demo 采用)
登录主应用获取认证 Cookie 后,直接访问 iframe 的 src 地址,绕过 wujie 微前端嵌套,使 page.locator() 正常工作:

// 登录主应用后,直接访问 NocoBase 页面
await page.goto('https://example.com/nocobase/admin/xxx?embed=true');
await page.waitForLoadState('networkidle');

// NocoBase 元素通过 aria-label 定位(无 id/name 属性)
await page.locator('[aria-label="action-Action-添加-create-app_config-table"]').click();
await page.locator('[aria-label*="app_config.app_id-AppId"] .ant-input').fill('test');

策略 B:wujie 微前端模式
当 iframe 直接访问不可用时,使用 Shadow DOM 穿透策略。

策略 C:普通页面模式
直接使用标准 Playwright API,无特殊处理。

4.5 产出物总览

完整的产出物目录结构如下:

test-docs/
├── requirement-overview.md          # 需求概述(3个功能模块)
├── architecture-info.md             # 页面架构探测结果
├── menu-url-map.md                  # 菜单导航地址映射
├── execution-report.md              # 探测执行报告
├── _playwright-probe/               # 浏览器探测脚本
│   ├── probe-architecture.ts
│   ├── probe-login-elements.ts
│   ├── probe-login-flow.ts
│   ├── probe-config-page.ts
│   └── probe-config-crud.ts
├── login/                           # 登录模块
│   ├── test-cases.md               # 3个测试用例
│   ├── automation-flow.md          # 流程步骤(选择器已回填)
│   └── element-map.md             # 5个元素 + 接口响应
├── navigation/                      # 导航模块
│   ├── test-cases.md
│   ├── automation-flow.md
│   └── element-map.md
└── vendor-config/                     # 供应商配置管理模块
    ├── test-cases.md               # 7个测试用例(TC-007~TC-013)
    ├── automation-flow.md
    └── element-map.md             # 18个元素 + 接口响应

tests/
├── login.spec.ts                    # 登录测试脚本
├── vendor-config.spec.ts              # 供应商配置管理测试脚本
├── fixtures/
│   └── login.json                  # 测试数据
└── helpers/
    └── auth.ts                     # 认证辅助函数

五、Spec 模式 vs 传统方式

维度传统手工编写Spec 模式(AI 驱动)
选择器来源手动查看 DOM 并编写AI 实际启动浏览器探测,自动识别最优选择器
断言依据凭经验猜测预期值基于实际拦截的接口响应数据
用例覆盖度依赖个人经验,容易遗漏AI 系统化生成,覆盖正向/反向/边界/异常
编写效率一个模块通常需要数小时三步流程,分钟级完成
可维护性脚本与需求脱节文档驱动,需求→用例→脚本全链路可追溯
微前端适配需深入了解框架原理自动检测并切换 Shadow DOM 穿透策略
知识沉淀知识在个人脑中结构化文档沉淀,团队可共享

六、适用场景

企业级 Web 应用的 E2E 测试:尤其是使用 Ant Design、Element UI 等组件库的中后台系统,AI 对这些组件的选择器模式有很好的识别能力。

微前端架构的复杂应用:wujie、qiankun 等微前端框架下,Shadow DOM 穿透选择器的编写是一个高门槛工作,AI 可以自动检测并适配。

快速建立自动化测试体系:对于还没有自动化测试的项目,这套方案可以在极短时间内建立起覆盖核心功能的测试用例集。

需求频繁变更的敏捷项目:Spec 模式的阶段解耦特性,使得页面改版只需重新执行步骤二更新选择器,无需从头编写脚本。

测试团队技能提升:降低 Playwright 脚本编写门槛,测试人员只需提供需求描述,AI 负责全部技术实现。

七、总结

这套方案的本质,是把 Spec 驱动开发的理念应用到了前端自动化测试领域:

  1. 选择器零猜测 — 所有选择器来源于 Playwright 对真实页面的探测结果
  2. 断言有据可依 — 接口响应的断言期望值来源于实际拦截到的响应内容
  3. 全链路可追溯 — 从需求描述到最终测试脚本,每一步都有对应的文档产出物
  4. 阶段解耦,灵活可控 — 三个步骤独立执行,用户可在每个阶段检查和修正
  5. 低门槛、高效率 — 测试人员只需提供需求描述,AI 负责全部技术实现

通过 Kiro Power,我们将自动化测试从「手工编码」提升到了「需求驱动的自动生成」,大幅降低了测试脚本的编写和维护成本。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐