AI+前端提效-09 AI赋能前端测试:单元测试、E2E测试自动生成,提升覆盖率

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 生成用例,成本大幅下降。

六、测试提效的团队落地建议

  1. 先给 AI 一个"测试风格样板":团队定好测试命名、断言风格,让 AI 模仿;
  2. 核心业务优先:让 AI 先补"钱相关、权限相关、核心链路"的测试;
  3. E2E 只测主链路:E2E 慢且脆,交给 AI 也只覆盖 80% 主流程;
  4. 测试代码也走 review:AI 生成的测试可能"测试了实现而非行为",要人工确认断言是否真的有意义。

七、小结

AI 让前端测试从"奢侈品"变成"日用品":

  • 单元测试:函数、组件、store 自动生成,边界场景 AI 帮你补全;
  • E2E 测试:业务流自动编写,主链路无人值守验证;
  • 覆盖率:AI 按报告差量补测,CI 门槛自动守护。

"代码写完,测试自动跟上",这才是 AI 时代前端工程化的完整形态。

下一篇进入第四板块------业务场景落地。第 10 篇先聊最常用的:AI 快速开发业务组件------表单、弹窗、列表、表格批量复用。


系列导航:

  • 第 8 篇:AI 自动化文档
  • 第 9 篇:AI 赋能前端测试(本篇)
  • 第 10 篇:AI 快速开发业务组件
相关推荐
计算机魔术师39 分钟前
OpenAI 评定 Astra 达到网络安全 Critical 能力阈值,将受限发布
前端
算了吧956940 分钟前
从“黑箱”到“透明”:答序科技如何用“诊断型”技术架构重构品牌AI可见度
人工智能·科技·架构
2601_9499506341 分钟前
考研英语资料太散?用小程序把真题、词汇和错题放到一起
人工智能·学习·考研·小程序·刷题·小程序推荐
ShineWinsu42 分钟前
对于OpenClaw:核心命令以及飞书钉钉等渠道的解析
人工智能·钉钉·飞书
JavaGuide42 分钟前
SpaceX 工程师的 AI Coding 玩法太牛了, 200 多个 Agent 并行!
前端·后端
小猪code1 小时前
frp 80/443 Web 服务两种部署模式实操笔记
前端·笔记
牧羊人.3331 小时前
动手学深度学习 02 | 手写数字识别
图像处理·人工智能·深度学习·算法
熊猫钓鱼>_>1 小时前
鸿蒙AI Agent新范式:从“对话式辅助”到“工程化代理”的Harness架构实战解析
人工智能·笔记·学习·华为·架构·harmonyos
JamesZhang800781 小时前
Chrome相关知识点
前端