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 整体架构
┌─────────────────────────────────────────────────────────────┐
│ 用户提供需求描述 │
└────────────────────────┬────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────┐
│ 步骤一:测试需求分解(对应 Spec 的 Requirements) │
│ 输出:test-cases.md + automation-flow.md │
│ (AI 系统化生成测试用例,覆盖正向/反向/边界/异常场景) │
└────────────────────────┬────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────┐
│ 步骤二:页面探测(对应 Spec 的 Design) │
│ 输出:element-map.md + screenshots/ + execution-report.md │
│ (Playwright 实际启动浏览器,探测真实选择器和接口响应) │
└────────────────────────┬────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────┐
│ 步骤三:脚本生成(对应 Spec 的 Tasks) │
│ 输出:xxx.spec.ts + fixtures/ + helpers/ + pages/ │
│ (选择器和断言值严格来源于步骤二的探测结果) │
└─────────────────────────────────────────────────────────────┘
3.2 关键设计决策
文档驱动,而非代码驱动:每个阶段的产出物都是结构化的 Markdown 文档。人类可读、可审查、可修改,阶段之间解耦,任何一步出错都可以单独重做。
选择器来源于真实探测,而非猜测:步骤二通过 Playwright 实际启动浏览器访问目标应用,探测并记录真实的页面元素选择器,同时记录备选选择器提高容错性。
接口响应优先的断言策略:前端 Toast 消息显示时间极短(2-5秒),自动化捕获不稳定。接口响应是确定性的、可靠的数据源,因此采用「接口响应拦截」作为第一优先级断言手段。
3.3 技术栈
| 组件 | 技术选型 | 说明 |
|---|---|---|
| AI 编排引擎 | Kiro Power | 通过 Kiro Power 定义 AI Agent 的行为规范和技能编排 |
| 测试框架 | Playwright | 支持多浏览器、自动等待、网络拦截 |
| 脚本语言 | TypeScript | 类型安全,与 Playwright 原生集成 |
| 文档格式 | Markdown | 通用、可读、可版本控制 |
3.4 Kiro Power 技能编排
我们将工作流拆分为一个总控文件和三个技能文件:
.kiro/steering/
├── 00-workflow-guide.md # 总控文件(始终加载,提供全局上下文)
├── 01-test-requirement-analysis.md # 步骤一:需求分解
├── 02-execute-automation-flow.md # 步骤二:页面探测
└── 03-generate-playwright-scripts.md # 步骤三:脚本生成
总控文件始终加载,为 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)识别最优选择器,同时记录备选选择器。
typescript
// 探测脚本核心逻辑示例
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):
markdown
## 元素列表
### 元素 1: 用户名输入框
- 推荐选择器: `#username`
- 选择器类型: id
- 备选选择器: `input[placeholder="用户名:"]`
- 关联测试用例: TC-001, TC-002, TC-004, TC-005
### 元素 2: 登录按钮
- 推荐选择器: `button:has-text("登录")`
- 选择器类型: text
- 备选选择器: `button.ant-btn-primary`
## 接口响应记录
### 正确账号密码登录 (TC-001)
- 请求: POST /api/login
- 响应:
{"code":"00000","msg":"success","success":true,"token":"xxx"}
### 错误密码登录 (TC-004)
- 请求: POST /api/login
- 响应:
{"code":"500","msg":"用户名或者密码错误","success":false}
同时生成关键步骤的截图,便于人工审查。这些产出物就是 Spec 模式中的「设计文档」--- 基于真实探测结果,而非凭空猜测。
4.3 步骤三:脚本生成(Tasks)
基于步骤一的测试用例和步骤二的元素映射,AI 生成可直接运行的 Playwright 测试脚本:
typescript
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('#rc-tabs-0-tab-account');
});
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/login')
&& resp.request().method() === 'POST'
);
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('#pwd', loginData.validPassword);
await page.click('button:has-text("登录")');
// 前端表单校验,不发送接口请求
const error = page.locator('.ant-form-item-explain-error');
await expect(error).toContainText('用户名是必填项');
});
test('TC-004: 错误密码登录', async ({ page }) => {
await page.fill('#username', loginData.validUser);
await page.fill('#pwd', 'wrong_password');
const responsePromise = page.waitForResponse(
resp => resp.url().includes('/api/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);
});
});
每一个选择器都可以追溯到 element-map.md,每一个断言值都可以追溯到实际拦截的接口响应。这就是 Spec 模式的力量 --- 实现严格遵循规格,不偏离设计意图。
4.4 微前端场景下的探测
在配置管理模块中,页面内容渲染在嵌套 iframe 中,探测脚本自动识别并适配:
探测发现:
1. 页面内容渲染在嵌套 iframe 中
2. iframe 加载时间约 10-15 秒,需要轮询等待
3. 主页面与 iframe 内的 CSS 前缀不同,需分别处理
4. 表单字段通过 aria-label 定位最可靠
5. 弹出层(Drawer/Modal)渲染在 iframe 内部
AI 自动生成适配 iframe 的选择器策略:
typescript
// iframe 内元素定位示例
const frame = page.frameLocator('iframe[name="content-frame"]');
// 使用 aria-label 定位表单字段
await frame.locator('[aria-label*="供应商"] .ant-select').click();
await frame.locator('[aria-label*="AppId"] input').fill('test-app-id');
await frame.locator('[aria-label*="AppKey"] input').fill('sk-test-key');
方案还内置了对 wujie(无界)微前端框架的自动检测:
typescript
// wujie 微前端检测逻辑
const wujieResult = await page.evaluate(() => {
const result = { isWujie: false, features: [] as string[] };
// 检测 wujie 相关 DOM 元素
const wujieElements = document.querySelectorAll(
'[class*="wujie"], wujie-app, [data-wujie-id]'
);
if (wujieElements.length > 0) {
result.isWujie = true;
}
// 检测 Shadow DOM 容器
document.querySelectorAll('*').forEach(el => {
if (el.shadowRoot && el.shadowRoot.querySelectorAll('*').length > 10) {
result.isWujie = true;
}
});
return result;
});
检测到 wujie 后,自动切换为 Shadow DOM 穿透选择器策略。
4.5 产出物总览
以登录模块为例,完整的产出物包括:
test-docs/
├── requirement-overview.md # 需求概述(6个功能模块)
├── execution-report.md # 探测执行报告
├── _playwright-probe/
│ └── probe-login.ts # 浏览器探测脚本
└── 登录/
├── test-cases.md # 8个测试用例(P0-P2)
├── automation-flow.md # 9个流程步骤(选择器已回填)
├── element-map.md # 7个元素 + 3条接口响应
└── screenshots/ # 关键步骤截图
tests/
├── login.spec.ts # 可运行的测试脚本
├── fixtures/login.json # 测试数据
├── helpers/auth.ts # 认证辅助函数
└── pages/login.page.ts # Page Object 封装
五、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 驱动开发的理念应用到了前端自动化测试领域:
- 选择器零猜测 --- 所有选择器来源于 Playwright 对真实页面的探测结果
- 断言有据可依 --- 接口响应的断言期望值来源于实际拦截到的响应内容
- 全链路可追溯 --- 从需求描述到最终测试脚本,每一步都有对应的文档产出物
- 阶段解耦,灵活可控 --- 三个步骤独立执行,用户可在每个阶段检查和修正
- 低门槛、高效率 --- 测试人员只需提供需求描述,AI 负责全部技术实现
通过 Kiro Power,我们将自动化测试从「手工编码」提升到了「需求驱动的自动生成」,大幅降低了测试脚本的编写和维护成本。