在前端,什么是测试

为什么要测试

现在的前端不只是“写页面”

表单要校验、要防重复提交

按钮要点了变 loading

弹窗要控制显示隐藏

组件之间要传值、通信

而这些都是逻辑,既然有逻辑,就可能出错

比如某个按钮失灵了,总不能每次都手动点一遍看事件报错吧

三层测试

别被“单元测试”“E2E”这些词吓到,其实就三类:

单元测试

测一个零件,也叫“单测”

比如你写了个 <Button /> 组件,就单独测它:

  • 文字对不对?
  • loading 时显示啥?
  • 点了会不会触发事件?

工具:Vitest(快且简单,和 Vite 适配)

集成测试

测几个零件合在一起

比如你有个登录表单,用了 <Input /><Button />,还要调 API

你得测:

  • 输入用户名密码,点登录,是不是调了 API?
  • 校验失败,有没有提示?

工具:还是 Vitest + Mock(假装调接口)

E2E 测试

模拟用户真实操作

比如你让用户从登录到下单,走完整流程

你写个脚本,让电脑自动:

  • 打开浏览器
  • 输入账号密码
  • 点登录
  • 加入购物车
  • 提交订单

工具推荐:Playwright(比 Puppeteer 更稳)

测试实践

我以前觉得 “写测试会不会比写代码还难”,直到我接触了组件库试了 Vitest,才发现其实很简单便捷()

先装个工具:

1
pnpm install -D vitest @testing-library/vue

然后写个测试文件 Button.test.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import { render, screen } from '@testing-library/vue'
import userEvent from '@testing-library/user-event'
import Button from './Button.vue'

// 测试1:按钮文字对不对?
test('应该显示“点击我”', () => {
render(Button, {
slots: { default: '点击我' }
})

// 看页面上有没有“点击我”这三个字
expect(screen.getByText('点击我')).toBeInTheDocument()
})

// 测试2:点了按钮,会不会触发 click 事件?
test('点击按钮应该触发 click', async () => {
const user = userEvent.setup()
const { emitted } = render(Button, {
slots: { default: '点击我' }
})

// 模拟用户点击
await user.click(screen.getByRole('button'))

// 检查有没有 emit 出 click 事件
expect(emitted().click).toBeTruthy()
})

写完后,命令行跑一下:

1
npx vitest

绿了就说明通过了

红的还会给你指出哪个测试点出了问题,并且期望值/类应该是什么

覆盖率

覆盖率就是“你写的代码,有没有被测试‘跑过’

若是 100%覆盖率,意味着你写的代码的所有逻辑都被完全执行过

覆盖率有以下四个指标

类型 说明
行覆盖率(Line Coverage) 一行代码是否被执行
语句覆盖率(Statement Coverage) 每个独立语句是否执行
分支覆盖率(Branch Coverage) 每个 if/else、三元运算符等分支是否覆盖
函数覆盖率(Function Coverage) 每个函数是否至少调用一次

img

但,它只表示 所有代码都被测试执行了一遍但不保证逻辑正确

假设你写了个函数:

1
2
3
function divide(a, b) {
return a / b;
}

你写了两个测试:

1
2
3
4
5
6
7
test('2除以1等于2', () => {
expect(divide(2, 1)).toBe(2);
});

test('3除以1等于3', () => {
expect(divide(3, 1)).toBe(3);
});

覆盖率:100%(只有一行代码,被执行了)

但问题:没测 b=0 的情况! 会返回 Infinity,实际应该抛错或提示

所以:覆盖率是“广度”,不是“深度”

而且不要为 100% 而写无意义的测试,比如:

1
2
3
test('1 + 1 应该等于 2', () => {
expect(1 + 1).toBe(2)
})

测试的目的是保障核心逻辑,不是凑数

把目标定在 80% 左右的行覆盖 + 100% 关键分支覆盖,比死磕 100% 更有价值。

测试的好处

敢改代码了,以前我最怕改老代码,总觉得“动一发而牵全身”

现在,只要测试通过,就有胆子提交

而且还可以加 CI(持续集成),每次提交代码,测试自动跑一遍,如果失败会自动打回,谁都不能合并

团队协作,一下子安心多了(bushi)