软件教程 2025年08月6日
0 收藏 0 点赞 757 浏览 5197 个字
摘要 :

文章目录 一、深入了解Jest断言体系 (一)断言和测试用例的紧密关系 (二)九大常用断言实战操作 (三)深度解析异步测试 二、Mock函数全方位解析 (一)为什么要用……




  • 一、深入了解Jest断言体系
    • (一)断言和测试用例的紧密关系
    • (二)九大常用断言实战操作
    • (三)深度解析异步测试
  • 二、Mock函数全方位解析
    • (一)为什么要用Mock函数
    • (二)三种Mock场景实际操作
    • (三)模拟函数的高级应用
  • 三、真实项目中的实践案例
    • (一)表单校验函数测试
    • (二)用户登录流程测试
  • 四、最佳实践和避坑小提示
    • (一)推荐做法
    • (二)常见问题
  • 五、探索Jest生态进阶玩法

今天给大家讲讲前端测试工具Jest里关键的两个部分——断言和模拟函数。很多小伙伴在接触Jest的时候,对这俩都有点摸不着头脑,别担心,这篇文章会通过超多实际例子,带大家搞懂它们的底层逻辑,教大家如何使用Jest 前端测试工具中断言与模拟函数。

一、深入了解Jest断言体系

(一)断言和测试用例的紧密关系

断言在测试里就像是一个严格的质量检查员,它的任务就是检查程序的每一个行为是不是和我们预先设想的一样。咱们看下面这段测试代码:

test(\'最简单的断言示例\', () => {
  expect(1 + 1).toBe(2);
});

这里面的expect().toBe()就是Jest里很典型的断言组合,它可是测试用例里验证结果的核心部分。就好比你做了一道数学题,得检查答案对不对,这个断言就是在帮你做这个检查。

(二)九大常用断言实战操作

根据大家平时使用的频率和不同的测试场景,我给大家整理了一份超实用的常用断言清单。

  • .toBe():基础值比较:用来判断两个基础值是否相等,像expect(42).toBe(42),就是看看前面的值是不是和后面的一样。
  • .toEqual():对象/数组深度比较:当需要比较两个对象或者数组里的每一个元素是不是都一样时,就用它。比如expect(obj).toEqual({a:1})
  • .toBeTruthy():验证是否为真值:可以用来检查某个值是不是“真的”,像字符串\'text\',它不是空的,所以expect(\'text\').toBeTruthy() 就会通过。
  • .toHaveLength():验证数组/字符串长度:想知道数组里有几个元素,或者字符串有多长,就用这个断言。例如expect(arr).toHaveLength(3) ,就是检查数组arr的长度是不是3。
  • .toThrow():验证抛出异常:有些函数在特定情况下应该报错,这时候就用它。比如expect(fn).toThrow() ,看看函数fn调用的时候会不会抛出异常。
  • .toContain():验证包含元素:检查数组或者字符串里有没有某个元素。像expect([\'a\',\'b\']).toContain(\'a\') ,就是看看数组里有没有\'a\'这个元素。
  • .toBeGreaterThan():数字大小比较:比较两个数字大小,expect(5).toBeGreaterThan(3) ,就是判断5是不是大于3。
  • .toMatch():正则匹配:当需要检查字符串是不是符合某个规则时,就用正则表达式来匹配。比如expect(\'abc\').toMatch(/b/) ,就是看看字符串\'abc\'里有没有包含字母b
  • .resolves/.rejects:异步代码验证:在处理异步操作的时候,用这个来检查异步操作是成功还是失败。例如await expect(promise).resolves.toBe(1)

这里有个很重要的对比要注意:

// 对象比较的陷阱案例
test(\'对象比较的坑\', () => {
  const obj = { id: 1 };

  expect(obj).toBe({ id: 1 });    // ✖️ 失败,比较对象引用
  expect(obj).toEqual({ id: 1 }); // ✔️ 正确方法
});

在比较对象的时候,toBe是比较对象的引用地址,两个看起来一样的对象,地址可能不同,所以一般用toEqual来比较对象里的内容。

(三)深度解析异步测试

在前端开发里,异步操作特别常见,下面给大家分享三种主流的异步测试方法。

  • Promise的优雅处理
test(\'获取用户数据\', () => {
  return fetchUser().then(user => {
    expect(user.name).toBe(\'John\');
  });
});

这里用fetchUser()这个异步函数获取用户数据,然后通过then来处理获取到的数据,再用断言检查数据是不是符合预期。

  • Async/Await的现代风
test(\'新版异步写法\', async () => {
  const user = await fetchUser();
  expect(user.id).toBeGreaterThan(0);
});

async/await让异步代码看起来更像同步代码,用await等待异步操作完成,拿到数据后再进行断言验证。

  • 回调地狱的解药
test(\'传统回调测试\', done => {
  fetchUser(user => {
    expect(user.age).toBe(30);
    done(); // 必须调用
  });
});

这种传统的回调方式也能进行异步测试,不过要记得在测试完成后调用done() ,不然测试可能不会结束。

二、Mock函数全方位解析

(一)为什么要用Mock函数

在真实的线上环境里,会有各种各样的不确定因素,像图片上传失败、接口返回异常、第三方服务超时等等。这时候,Mock函数就派上用场啦!它可以帮我们隔离外部依赖,模拟出各种测试场景,还能捕获函数调用时的参数,测试一些边界情况。

(二)三种Mock场景实际操作

  • 基础函数模拟
// 创建模拟函数
const mockFn = jest.fn();

// 设置返回值为固定值
mockFn.mockReturnValue(42);
console.log(mockFn()); // 42

// 动态返回值
mockFn.mockImplementation((n) => n * 2);
console.log(mockFn(3)); // 6

// Promise模拟
mockFn.mockResolvedValue(\'success\');
await mockFn().then(data => {
  console.log(data); // \'success\'
});

先创建一个模拟函数mockFn,然后可以给它设置固定的返回值,也能让它根据传入的参数动态返回值,还能模拟成Promise形式。

  • 模块方法劫持:在需要模拟第三方模块的时候,这个方法特别有用。
// userAPI.js
export const getUser = () => {
  // 真实网络请求...
};

// 测试文件
import { getUser } from \'./userAPI\';

jest.mock(\'./userAPI\', () => ({
  getUser: jest.fn().mockResolvedValue({
    name: \'Mock用户\'
  })
}));

test(\'模块模拟测试\', async () => {
  const user = await getUser();
  expect(user.name).toContain(\'Mock\');
});

这里模拟了getUser这个函数,让它返回我们预设的数据,方便在测试的时候使用。

  • 高阶函数追踪器
const mathUtils = {
  multiply: (a, b) => a * b,
};

test(\'函数调用追踪\', () => {
  mathUtils.multiply = jest.fn();
  mathUtils.multiply(2, 3);

  expect(mathUtils.multiply)
    .toHaveBeenCalledWith(2, 3);  // ✔️验证调用参数

  expect(mathUtils.multiply.mock.calls.length)
    .toBe(1);  // 直接访问Mock属性
});

通过把mathUtils.multiply变成模拟函数,我们可以追踪它有没有被调用,以及调用时的参数是什么。

(三)模拟函数的高级应用

  • 模拟不同的连续返回值
const mockRoll = jest.fn()
    .mockReturnValueOnce(1)
    .mockReturnValueOnce(2)
    .mockReturnValue(3);

// 测试结果
mockRoll(); // 1
mockRoll(); // 2
mockRoll(); // 3

这样设置后,每次调用mockRoll函数,返回的值都不一样,方便测试一些依赖多次调用返回不同结果的场景。

  • 复杂模块的部分模拟
// 原模块功能保留,只模拟部分方法
jest.mock(\'axios\', () => {
  const actual = jest.requireActual(\'axios\');
  return {
    ...actual,
    get: jest.fn().mockResolvedValue({ data: \'mock\' }),
  };
});

对于像axios这样比较复杂的模块,我们可以只模拟其中的部分方法,其他方法还是保留原来的功能。

三、真实项目中的实践案例

(一)表单校验函数测试

// 表单验证函数
function validateForm(values) {
  const errors = {};
  if (!values.username) errors.username = \'必填字段\';
  if (values.age < 18) errors.age = \'未满18岁\';
  return errors;
}

// 测试用例
test(\'表单验证应返回错误信息\', () => {
  expect(validateForm({}))
    .toEqual({
      username: \'必填字段\',
      age: \'未满18岁\'
    });

  expect(validateForm({ username: \'Tom\', age: 20 }))
    .toEqual({});
});

这个表单验证函数用来检查表单里的数据合不合格,通过测试用例可以验证它在不同情况下返回的结果是不是正确。

(二)用户登录流程测试

// 测试用户登录流程
test(\'用户登录成功流程\', async () => {
  // 模拟登录接口
  mockLoginAPI.mockResolvedValue({
    success: true,
    token: \'fake-token\'
  });

  const result = await login(\'user\', \'pass\');

  expect(mockLoginAPI)
    .toHaveBeenCalledWith(\'user\', \'pass\');

  expect(localStorage.setItem)
    .toHaveBeenCalledWith(\'token\', \'fake-token\');
});

这里模拟了登录接口的返回数据,然后检查登录函数有没有正确调用接口,以及登录成功后有没有把token存到本地存储里。

四、最佳实践和避坑小提示

(一)推荐做法

在对象校验的时候,优先用expect().toEqual() ;给Mock函数命名的时候,加上mock前缀,像mockFetch,这样别人一看就知道是Mock函数;用.toHaveBeenCalledTimes() 来验证函数被调用的次数;每个测试案例最好用beforeEach来重置Mock,保证测试之间不会互相影响。

(二)常见问题

比较对象的时候,一定不要用toBe,要用toEqual,不然很可能因为对象引用的问题导致测试出错;写异步测试的时候,别忘了async/await ,不然可能会出现假通过的情况;每次测试完,记得在beforeEach里调用jest.clearAllMocks() ,不然Mock的结果可能会残留,影响下一次测试;对于那些没有外部依赖的纯函数,直接测试就行,不用Mock。

五、探索Jest生态进阶玩法

如果大家还想更深入地学习Jest,下面这些方向可以去探索一下。

  • 快照测试(Snapshot Testing):可以把UI组件的输出结果拍个“快照”,下次测试的时候对比一下,看看有没有变化,就像给组件拍照片,方便检查有没有改坏。
  • 覆盖率报告(Coverage Report):通过--coverage这个参数,能生成代码的覆盖率报告,看看哪些代码被测试覆盖到了,哪些还没有。
  • 定时器模拟(Fake Timers):在测试涉及到setTimeout这些时间逻辑的代码时,用它来模拟时间,让测试更准确。
  • E2E测试整合:可以把Jest和Cypress、Puppeteer这些工具一起用,实现更全面的测试。

在开发过程中,单元测试覆盖率从0提升到100%的时候,你会发现代码质量有质的飞跃!在现在持续集成的大环境下,养成良好的测试习惯不仅能让代码更靠谱,还是你技术实力的体现呢!大家一定要重视起来,每个高质量的测试用例都是项目稳定运行的保障!

微信扫一扫

支付宝扫一扫

版权: 转载请注明出处:https://www.zuozi.net/6811.html

管理员

相关推荐
2025-08-06

文章目录 一、Promise基础回顾 二、Promise 与 axios 结合使用场景及方法 (一)直接返回 axios …

269
2025-08-06

文章目录 一、模块初始化时的内部机制 二、常见导出写法的差异分析 (一)写法一:module.exports…

107
2025-08-06

文章目录 一、ResizeObserver详解 (一)ResizeObserver是什么 (二)ResizeObserver的基本用法 …

683
2025-08-06

文章目录 一、前期准备工作 (一)下载相关文件 (二)安装必要工具 二、处理扣子空间生成的文件…

338
2025-08-06

文章目录 一、官方文档 二、自动解包的数据类型 ref对象:无需.value即可访问 reactive对象:保持…

371
2025-08-06

文章目录 一、Hooks的工作原理 二、在if语句中使用Hook会出什么岔子? 三、React官方的Hook使用规…

843
发表评论
暂无评论

还没有评论呢,快来抢沙发~

助力内容变现

将您的收入提升到一个新的水平

点击联系客服

在线时间:08:00-23:00

客服QQ

122325244

客服电话

400-888-8888

客服邮箱

122325244@qq.com

扫描二维码

关注微信客服号