AI 赋能前端测试:单元测试、E2E 测试自动生成,提升覆盖率
本文是《AI+前端提效》系列的第 9 篇,工程化板块收官之作。测试是前端团队"最想做但最没时间做"的事。AI 的介入,让"补测试"从"痛苦的义务"变成"顺手的习惯"。
一、为什么前端测试总是"缺席"?
真实的前端团队测试现状:
- 单元测试覆盖率不足 20% 是常态;
- 业务迭代快,测试用例写完就过期;
- 写测试比写功能还费时间,没人愿意干;
- 测试代码质量差,跑了也等于没跑。
但测试的价值无可替代:它是重构的安全网、回归的守护者、新人改代码的底气。
AI 能做什么?不是"替你思考测试策略",而是把"写测试代码"这个体力活自动化------你负责想测什么,AI 负责把用例写出来。
二、AI 自动生成 Jest 单元测试代码
2.1 基础场景:纯函数/工具函数测试
text
请为以下工具函数生成 Jest 单元测试:
(粘贴 utils/format.ts 代码)
要求:
1. 覆盖正常输入、边界输入(空、null、undefined、超长)
2. 覆盖异常输入(非法格式、类型错误)
3. 使用 describe/it/expect 结构
4. 断言要具体,不要用"万能断言"
5. 输出到 __tests__/format.test.ts
示例:假设工具函数是金额格式化
typescript
// utils/format.ts
export function formatPrice(price: number | string): string {
const num = Number(price)
if (isNaN(num)) return '--'
return `¥ ${num.toFixed(2)}`
}
AI 生成的测试:
typescript
// __tests__/format.test.ts
import { formatPrice } from '../utils/format'
describe('formatPrice', () => {
it('正常数字', () => {
expect(formatPrice(199)).toBe('¥ 199.00')
expect(formatPrice('299.5')).toBe('¥ 299.50')
})
it('小数保留两位', () => {
expect(formatPrice(10.005)).toBe('¥ 10.01')
})
it('0 值', () => {
expect(formatPrice(0)).toBe('¥ 0.00')
})
it('非法输入返回占位符', () => {
expect(formatPrice('abc')).toBe('--')
expect(formatPrice(null as unknown as string)).toBe('--')
expect(formatPrice(undefined as unknown as string)).toBe('--')
})
it('空字符串返回占位符', () => {
expect(formatPrice('')).toBe('--')
})
})
2.2 Vue 组件单元测试
text
请为以下 Vue3 组件生成单元测试(使用 @vue/test-utils + Vitest):
(粘贴 SearchInput.vue 代码)
要求:
1. 测试 v-model 双向绑定
2. 测试防抖:使用 fake timers 验证 500ms 后触发 search
3. 测试清空按钮
4. 测试回车立即搜索
5. 测试卸载时清除计时器
typescript
// __tests__/SearchInput.test.ts
import { mount } from '@vue/test-utils'
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
import SearchInput from '../SearchInput.vue'
describe('SearchInput', () => {
beforeEach(() => {
vi.useFakeTimers()
})
afterEach(() => {
vi.useRealTimers()
})
it('输入后防抖触发 search', async () => {
const wrapper = mount(SearchInput, {
props: { modelValue: '' }
})
const input = wrapper.find('input')
await input.setValue('vue')
expect(wrapper.emitted('search')).toBeUndefined()
vi.advanceTimersByTime(500)
expect(wrapper.emitted('search')?.[0]).toEqual(['vue'])
})
it('连续输入只触发一次', async () => {
const wrapper = mount(SearchInput)
const input = wrapper.find('input')
await input.setValue('a')
await input.setValue('ab')
await input.setValue('abc')
vi.advanceTimersByTime(500)
expect(wrapper.emitted('search')?.length).toBe(1)
expect(wrapper.emitted('search')?.[0]).toEqual(['abc'])
})
it('回车立即触发 search', async () => {
const wrapper = mount(SearchInput, {
props: { modelValue: 'keyword' }
})
await wrapper.find('input').trigger('keyup.enter')
expect(wrapper.emitted('search')?.[0]).toEqual(['keyword'])
})
it('卸载时清除计时器', async () => {
const wrapper = mount(SearchInput)
await wrapper.find('input').setValue('test')
wrapper.unmount()
vi.advanceTimersByTime(1000)
expect(wrapper.emitted('search')).toBeUndefined()
})
})
2.3 Pinia store 测试
text
为以下 Pinia store 生成测试:
- 初始状态
- action 成功/失败分支
- getter 派生逻辑
(粘贴 stores/user.ts 代码)
三、自动编写 E2E 自动化测试用例
3.1 用 Playwright 生成 E2E 用例
text
请为以下业务场景编写 Playwright E2E 测试用例(TypeScript):
场景:登录 → 进入订单列表 → 搜索关键词 → 点击详情 → 校验详情字段
要求:
1. 使用 test.describe 分组
2. 每个用例独立、可重复运行
3. 合理使用 locator(优先 role/text,避免脆弱选择器)
4. 关键操作加断言
5. 输出到 e2e/order-flow.spec.ts
typescript
// e2e/order-flow.spec.ts
import { test, expect } from '@playwright/test'
test.describe('订单管理流程', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/login')
await page.getByLabel('用户名').fill('admin')
await page.getByLabel('密码').fill('admin123')
await page.getByRole('button', { name: '登录' }).click()
await expect(page).toHaveURL(/\/dashboard/)
})
test('搜索订单并查看详情', async ({ page }) => {
await page.goto('/orders')
await expect(page.getByRole('table')).toBeVisible()
// 搜索
await page.getByPlaceholder('请输入订单号').fill('ORD-20260825-001')
await page.getByRole('button', { name: '查询' }).click()
await expect(page.getByText('ORD-20260825-001')).toBeVisible()
// 进入详情
await page.getByRole('button', { name: '详情' }).first().click()
await expect(page.getByRole('heading', { name: '订单详情' })).toBeVisible()
await expect(page.getByText('收货人:张三')).toBeVisible()
await expect(page.getByText('订单金额:¥ 199.00')).toBeVisible()
})
test('空搜索结果提示', async ({ page }) => {
await page.goto('/orders')
await page.getByPlaceholder('请输入订单号').fill('NOT-EXIST-000')
await page.getByRole('button', { name: '查询' }).click()
await expect(page.getByText('暂无数据')).toBeVisible()
})
})
3.2 从用户故事自动生成用例
text
根据以下用户故事生成 E2E 测试用例:
"作为管理员,我可以在用户管理页禁用某个用户,禁用后该用户无法登录"
请输出:
1. 前置条件(数据准备)
2. 测试步骤
3. 每个步骤的断言
4. 对应 Playwright 代码
四、针对业务逻辑、边界场景补充测试
4.1 让 AI 找"测试盲区"
text
请 review 这个函数的实现,找出 5 个容易被忽略的边界场景,并补充测试:
(粘贴业务函数代码,如优惠券计算、权限判断)
4.2 典型边界场景清单
AI 特别擅长发现这些边界:
| 场景类型 | 示例 |
|---|---|
| 空值/未定义 | 列表为空、对象属性缺失 |
| 极值 | 超大数字、超长字符串、0 长度 |
| 特殊字符 | 中文、emoji、HTML 标签、SQL 注入字符 |
| 时间边界 | 跨年、跨月、时区、闰年 |
| 并发/竞态 | 快速连点、重复提交、请求乱序 |
| 权限边界 | 无权限、部分权限、超管 |
4.3 示例:权限判断函数补充测试
text
请为以下权限函数补充边界测试:
(粘贴 checkPermission 代码)
特别关注:
- 用户无任何角色
- 角色为空数组
- 需要的权限为通配符 *
- 大小写不一致
typescript
// 假设函数
export function checkPermission(userRoles: string[], required: string | string[]): boolean {
if (userRoles.includes('admin')) return true
const requiredList = Array.isArray(required) ? required : [required]
return requiredList.some(r => userRoles.includes(r))
}
// AI 补充的边界测试
describe('checkPermission 边界', () => {
it('admin 通配', () => {
expect(checkPermission(['admin'], 'anything')).toBe(true)
})
it('无角色返回 false', () => {
expect(checkPermission([], 'user:edit')).toBe(false)
})
it('多权限任一满足即可', () => {
expect(checkPermission(['user:view'], ['user:edit', 'user:view'])).toBe(true)
})
it('全部不满足返回 false', () => {
expect(checkPermission(['user:view'], ['user:edit', 'user:delete'])).toBe(false)
})
it('required 为字符串', () => {
expect(checkPermission(['user:edit'], 'user:edit')).toBe(true)
})
})
五、快速提升项目测试覆盖率
5.1 覆盖率分析与 AI 补测
bash
# 先生成覆盖率报告
npx vitest run --coverage
然后让 AI 基于报告补测:
text
当前测试覆盖率报告显示 src/utils/date.ts 分支覆盖率只有 40%。
请读取该文件,找出未覆盖的分支并补充测试用例。
(粘贴覆盖率报告片段 + 文件代码)
5.2 生成"差量测试"------只补新代码的测试
text
以下代码是新加的(diff 片段),请为新增逻辑生成对应测试,
不要为未变更的代码重复生成测试。
5.3 CI 中强制覆盖率门槛
yaml
# vitest 配置
test: {
coverage: {
provider: 'v8',
reporter: ['text', 'html', 'lcov'],
include: ['src/**/*.{ts,vue}'],
exclude: ['src/main.ts', 'src/types/**'],
thresholds: {
lines: 80,
functions: 80,
branches: 70,
statements: 80
}
}
}
覆盖率不达标则 CI 失败,倒逼团队持续补测------配合 AI 生成用例,成本大幅下降。
六、测试提效的团队落地建议
- 先给 AI 一个"测试风格样板":团队定好测试命名、断言风格,让 AI 模仿;
- 核心业务优先:让 AI 先补"钱相关、权限相关、核心链路"的测试;
- E2E 只测主链路:E2E 慢且脆,交给 AI 也只覆盖 80% 主流程;
- 测试代码也走 review:AI 生成的测试可能"测试了实现而非行为",要人工确认断言是否真的有意义。
七、小结
AI 让前端测试从"奢侈品"变成"日用品":
- 单元测试:函数、组件、store 自动生成,边界场景 AI 帮你补全;
- E2E 测试:业务流自动编写,主链路无人值守验证;
- 覆盖率:AI 按报告差量补测,CI 门槛自动守护。
"代码写完,测试自动跟上",这才是 AI 时代前端工程化的完整形态。
下一篇进入第四板块------业务场景落地。第 10 篇先聊最常用的:AI 快速开发业务组件------表单、弹窗、列表、表格批量复用。
系列导航:
- 第 8 篇:AI 自动化文档
- 第 9 篇:AI 赋能前端测试(本篇)
- 第 10 篇:AI 快速开发业务组件