Skip to content

Jest 单元测试:前端开发者的代码守护神

更新: 6/14/2026 字数: 0 字 时长: 0 分钟

一、从一个真实的开发痛点说起

你写了一个工具函数 formatPrice,把数字格式化成价格。本地手动试了几个数,看着没问题,就提交上线了。

三周后,产品迭代,同事改动了相邻的代码,顺手"优化"了你的函数。结果线上出现了 ¥NaN,用户投诉,你被拉进群里背锅——可你明明三周前测过啊。

问题在于:手动测试是一次性的,人一走它就失效了。 你需要一个"永远在岗、每次改代码都自动帮你重测一遍"的机器人。这个机器人,就是 Jest。

Jest 是什么

二、什么是 Jest

Jest 是由 Meta(原 Facebook)开源的一款 JavaScript 测试框架。它的定位很明确:让你用极少的配置,就能给前端代码写自动化测试。

所谓"单元测试",就是把代码拆成一个个最小的"单元"(通常是一个函数、一个组件),单独验证每个单元"输入什么、就该输出什么"。Jest 就是帮你写、跑、管这些单元测试的工具。

它之所以成为前端测试的事实标准,靠的是这几个特点:

  • 开箱即用:自带测试运行器、断言库、Mock 工具、覆盖率统计,装一个就够,不用东拼西凑。
  • 零配置起步:大部分项目装完就能跑,不用折腾一堆配置文件。
  • :自动并行跑测试,还能只跑"改动相关"的那部分。
  • 生态好:配合 React Testing Library、Vue Test Utils 等,能测组件、测交互。

一句话定义:Jest 是一个开箱即用的 JavaScript 测试框架,帮前端开发者把"代码该有的行为"写成可自动执行的检查,每次改动后一键验证。

三、为什么前端开发者需要它

Jest 有什么用

1. 提前抓虫,把 bug 挡在上线前

测试在你本地、在 CI 流水线里就跑了。函数逻辑一旦写错,测试立刻飘红,根本到不了用户面前。

2. 重构不慌

想优化一段陈年老代码却不敢动?只要测试齐全,你大胆改,改完跑一遍测试,全绿就说明行为没变,红了就说明你改坏了。测试是重构的安全网。

3. 测试即"活文档"

一份好的测试用例,清楚写明了"这个函数在各种输入下应该返回什么"。新人接手代码,看测试就能秒懂函数的预期行为,比注释还可靠——因为注释会过期,测试不会(过期了就飘红了)。

4. 跨团队协作更放心

多人协作时,别人改了公共模块,你的测试会替你"站岗"。谁改坏了你的功能,提交时测试立刻报警。

四、五分钟跑起第一个测试

第 1 步:安装

bash
npm install --save-dev jest

package.json 里加一条脚本:

json
{
  "scripts": {
    "test": "jest"
  }
}

第 2 步:写一个被测函数

新建 sum.js:

javascript
function sum(a, b) {
  return a + b;
}

module.exports = sum;

第 3 步:写测试文件

新建 sum.test.js(Jest 会自动识别 .test.js.spec.js 结尾的文件):

javascript
const sum = require('./sum');

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

第 4 步:运行

bash
npm test

你会看到一行绿色的 ✓ 1 加 2 应该等于 3。恭喜,你的第一个自动化测试跑通了。

这三行测试代码里藏着 Jest 最核心的三件套:test() 定义一个测试用例,expect() 包裹"实际结果",toBe() 是"匹配器",声明你期望的值。

五、读懂一个测试的骨架:AAA 法则

AAA 法则

写测试别东一榔头西一棒子,业界公认的清晰结构叫 AAA 法则,任何一个测试都能拆成这三段:

  1. Arrange(准备):准备好测试要用的数据和环境。
  2. Act(执行):调用被测的那个函数/方法。
  3. Assert(断言):检查结果是不是和预期一致。
javascript
test('购物车计算总价', () => {
  // Arrange 准备数据
  const cart = [
    { name: '苹果', price: 5, count: 2 },
    { name: '牛奶', price: 10, count: 1 },
  ];

  // Act 执行操作
  const total = calculateTotal(cart);

  // Assert 断言验证
  expect(total).toBe(20);
});

养成这个习惯,你的测试别人一眼就能读懂。

组织测试:describe 与常用钩子

describe 把相关的测试归到一组,用钩子函数处理重复的准备/清理工作:

javascript
describe('用户模块', () => {
  beforeEach(() => {
    // 每个测试运行前都会执行,常用来重置数据
  });

  afterEach(() => {
    // 每个测试运行后执行,常用来清理
  });

  test('能正确创建用户', () => { /* ... */ });
  test('能正确删除用户', () => { /* ... */ });
});

六、常用匹配器(Matchers)速查

匹配器就是 expect(...) 后面跟的那个判断方法,决定你"怎么比"。下面是高频的几类:

匹配器含义示例
toBe严格相等(适合数字、字符串)expect(2 + 2).toBe(4)
toEqual值相等(适合对象、数组,递归比较内容)expect(obj).toEqual({ a: 1 })
toBeTruthy / toBeFalsy是否为"真值"/"假值"expect(value).toBeTruthy()
toContain数组/字符串是否包含某项expect(list).toContain('apple')
toThrow是否抛出异常expect(() => fn()).toThrow()
toHaveBeenCalled函数是否被调用过(配合 Mock)expect(mockFn).toHaveBeenCalled()
not取反,接在前面expect(1).not.toBe(2)

小坑提醒:比较对象或数组时,千万别用 toBe(它比的是"是不是同一个引用"),要用 toEqual(比的是"内容是否一样")。这是新手最常踩的雷。

七、处理异步代码

前端到处是异步(接口请求、定时器)。Jest 测异步有两种常见写法:

async / await(最推荐,可读性最好):

javascript
test('能获取到用户数据', async () => {
  const data = await fetchUser(1);
  expect(data.name).toBe('张三');
});

测试 Promise:

javascript
test('请求成功返回数据', () => {
  return expect(fetchUser(1)).resolves.toHaveProperty('name');
});

关键点:异步测试一定要 awaitreturn,否则 Jest 不会等异步结束,测试会"假通过"——这是另一个高频坑。

八、Mock:用"替身"隔离外部依赖

Mock 模拟

单元测试讲究"只测自己这一块"。可你的函数往往要调接口、读数据库——这些又慢又不稳定,还可能没网。怎么办?用 Mock(模拟),给真实依赖找个"替身"。

Mock 的作用,就是把真实的外部依赖换成一个你能完全掌控的假对象:让它想返回什么就返回什么,瞬间响应,还能记录"它被调用了几次、传了什么参数"。

模拟一个函数:

javascript
test('点击按钮会触发回调', () => {
  const mockCallback = jest.fn();          // 造一个假函数
  handleClick(mockCallback);

  expect(mockCallback).toHaveBeenCalled();        // 验证被调用了
  expect(mockCallback).toHaveBeenCalledTimes(1);  // 验证调用了 1 次
});

模拟一个接口请求模块:

javascript
jest.mock('./api');           // 把整个 api 模块替换成 Mock
import { fetchUser } from './api';

test('展示用户名', async () => {
  fetchUser.mockResolvedValue({ name: '李四' });  // 指定假返回

  const name = await getUserName(1);
  expect(name).toBe('李四');
});

这样一来,测试不依赖真实网络,又快又稳定,还能轻松模拟"接口报错"这种平时难复现的场景。

九、测试覆盖率:看看哪里还没测到

测试覆盖率

跑测试时加个参数,Jest 会生成一份"覆盖率报告",告诉你代码有多少被测到了:

bash
npx jest --coverage

报告里有几个关键指标:

  • 语句覆盖(Statements):有多少行代码被执行到了。
  • 分支覆盖(Branches):if/else、三元运算的每个分叉都走到了吗。
  • 函数覆盖(Functions):有多少函数被调用过。
  • 行覆盖(Lines):覆盖到的代码行比例。

理性看待覆盖率:它能帮你发现"完全没被测到的死角",但高覆盖率 ≠ 高质量。100% 覆盖也可能断言写得敷衍。把它当"地图"用,优先覆盖核心逻辑,别为了凑数字写无意义的测试。

十、给前端的实战建议

  • 从工具函数开始:纯函数(给定输入必有固定输出)最好测,先从 utils 目录练手,最容易获得成就感。
  • 测行为,别测实现细节:测"用户点击后看到了什么",而不是"内部某个变量变成了几"。这样重构时测试才不会动不动就崩。
  • 一个测试只验证一件事:用例小而专注,失败时一眼就知道哪儿坏了。
  • 测试名写成人话:test('价格为负数时应抛出错误') 远胜 test('test1')
  • 配合 CI 自动跑:把 npm test 接入提交/合并流程,让测试成为代码进主干前的强制关卡。
  • 别追求 100% 覆盖:核心逻辑、易错边界重点测;一些简单的样板代码不必强求。

十一、一句话总结

Jest 之于前端,就像一个不知疲倦、永远在岗的代码质检员。你只需把"代码本该如何表现"写成一条条测试,它就会在你每次改动后自动重跑一遍,替你守住质量底线。写测试当下看似多花了几分钟,换来的是日后重构的底气和半夜不被报警电话叫醒的安稳——这笔投资,前端开发者越早开始越划算。