0

0

Jest 测试中动态切换手动 Mock 与真实模块实现

聖光之護

聖光之護

发布时间:2025-10-29 12:18:21

|

412人浏览过

|

来源于php中文网

原创

jest 测试中动态切换手动 mock 与真实模块实现

本文详细阐述了在 Jest 测试框架中,当存在模块的手动 Mock 实现时,如何在特定测试用例或测试套件中选择性地使用模块的真实实现。核心解决方案是结合使用 jest.dontMock(moduleName) 和 jest.resetModules(),以确保测试环境的灵活性和准确性。

挑战与需求

在 Jest 中进行单元测试时,我们经常会为外部依赖(如网络请求库 Axios、数据库连接等)创建手动 Mock (__mocks__/moduleName.js)。这种方式能有效地隔离被测模块,使测试聚焦于其自身逻辑,避免真实依赖带来的副作用或性能开销。

然而,在某些场景下,我们可能需要验证模块与真实依赖的集成情况,或者在同一个测试文件中,某些测试用例需要 Mock,而另一些则需要真实的模块实现。直接在测试用例内部使用 jest.mock("moduleName", () => jest.requireActual("moduleName")) 往往无法达到预期效果,因为 Jest 的模块加载机制在测试执行前就已经处理了手动 Mock,后续的 jest.mock 调用可能无法覆盖已缓存的 Mock。

解决方案:jest.dontMock 与 jest.resetModules

要实现动态切换,我们需要结合使用 Jest 提供的两个关键 API:

  1. jest.dontMock(moduleName): 此函数指示 Jest 不对指定模块进行 Mock。它会取消对该模块的任何现有 Mock 定义,包括手动 Mock。
  2. jest.resetModules(): 此函数会清除 Jest 的模块注册表(即所有已导入模块的缓存)。这至关重要,因为 jest.dontMock 仅指示 Jest "下次" 不要 Mock,而要让其生效,我们需要强制 Jest 重新加载模块。

通过在测试用例或 beforeEach 钩子中先调用 jest.resetModules() 清除模块缓存,再调用 jest.dontMock('moduleName'),可以确保当模块被重新导入时,Jest 会加载其真实实现而非 Mock。

实战示例

以下通过一个使用 axios 的具体例子,演示如何在一个测试套件中同时测试 Mocked axios 和真实 axios 的行为。

1. 设置手动 Mock (__mocks__/axios.js)

首先,创建一个 axios 的手动 Mock。这个 Mock 会拦截 axios.get 调用并返回一个预设的假数据。

Kaiber
Kaiber

Kaiber是一个视频生成引擎,用户可以根据自己的图片或文字描述创建视频

下载
// __mocks__/axios.js
let mockAxios = jest.genMockFromModule('axios');

// 为 get 方法提供一个 Mock 实现
mockAxios.get = jest.fn().mockResolvedValue({ data: 'fake' });

export default mockAxios;

2. 被测试的模块 (main.ts)

这是一个简单的模块,它使用 axios 发起一个 GET 请求。

// main.ts
import axios from 'axios';

export function main() {
  return axios.get('https://jsonplaceholder.typicode.com/todos/1');
}

3. 测试文件 (main.test.ts)

在这个测试文件中,我们将编写两个测试用例:一个使用 Mocked axios,另一个使用真实的 axios。

// main.test.ts
import { main } from './main'; // 导入被测试的模块

describe('Axios 模块切换测试', () => {
  // 在每个测试用例运行前重置模块缓存
  beforeEach(() => {
    jest.resetModules();
  });

  test('应该使用 Mocked Axios', async () => {
    // 默认情况下,如果存在手动 Mock,Jest 会使用它
    const result = await main();
    expect(result.data).toEqual('fake'); // 验证是否返回 Mock 数据
    console.log('Mocked Axios 结果:', result.data);
  });

  test('应该使用真实的 Axios', async () => {
    // 在这个测试用例中,我们取消对 axios 的 Mock
    jest.dontMock('axios');

    // 由于 beforeEach 中已经 resetModules,这里重新导入 main 模块
    // 此时 main 模块内部引用的 axios 将是真实的 axios
    const { main: actualMain } = await import('./main');
    const result = await actualMain();

    // 验证是否返回真实数据(通常是来自 API 的 JSON 对象)
    expect(result).toHaveProperty('data');
    expect(result.data).toHaveProperty('userId');
    expect(result.data).toHaveProperty('id');
    console.log('真实 Axios 结果:', result.data);
  });
});

4. 运行测试与结果分析

运行上述测试后,你将看到类似以下的输出:

 console.log
    Mocked Axios 结果: fake

 console.log
    真实 Axios 结果: { userId: 1, id: 1, title: 'delectus aut autem', completed: false }

 PASS  ./main.test.ts
  Axios 模块切换测试
    ✓ 应该使用 Mocked Axios (XX ms)
    ✓ 应该使用真实的 Axios (YY ms)

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        ZZ s

从输出可以看出,第一个测试用例成功地使用了手动 Mock 返回了 'fake' 数据,而第二个测试用例则成功地发起了真实的网络请求,并从 jsonplaceholder.typicode.com 获取了真实的数据。这证明了 jest.dontMock 和 jest.resetModules 组合的有效性。

注意事项与最佳实践

  1. jest.resetModules() 的位置: 将 jest.resetModules() 放在 beforeEach 钩子中是推荐的做法。这确保了每个测试用例都在一个干净的模块环境中启动,避免了测试之间的状态污染。
  2. jest.dontMock() 的作用域: jest.dontMock() 调用会影响当前测试文件或 describe 块中后续的模块加载。如果你在一个 describe 块的 beforeAll 或 beforeEach 中调用它,那么该块内的所有测试都将受到影响。
  3. 模块重新导入: 当你调用 jest.resetModules() 后,任何之前导入的模块都需要重新导入才能反映新的 Mock/un-Mock 状态。在示例中,我们通过 await import('./main') 重新获取了 main 模块。
  4. 性能考量: jest.resetModules() 会清除所有模块缓存,这意味着 Jest 需要重新加载所有依赖。对于大型项目,频繁地在每个测试用例中调用 jest.resetModules() 可能会略微增加测试运行时间。在实际应用中,应权衡灵活性和性能,仅在必要时使用。
  5. 替代方案: 对于一些简单的场景,如果只是想在 Mock 中调用真实实现的一部分,jest.requireActual() 可以在 Mock 文件内部使用,例如 const actualAxios = jest.requireActual('axios'); mockAxios.post = actualAxios.post;。但这与本文讨论的动态切换整个模块 Mock 的场景有所不同。

总结

在 Jest 测试中,通过巧妙地结合使用 jest.dontMock(moduleName) 和 jest.resetModules(),我们可以实现对模块 Mock 行为的精细控制。这种方法允许开发者在同一测试套件中灵活地切换使用模块的 Mock 实现或真实实现,从而满足更复杂的测试需求,确保测试的全面性和准确性。理解并掌握这一技巧,将极大地提升 Jest 测试的灵活性和效率。

相关专题

更多
c语言const用法
c语言const用法

const是关键字,可以用于声明常量、函数参数中的const修饰符、const修饰函数返回值、const修饰指针。详细介绍:1、声明常量,const关键字可用于声明常量,常量的值在程序运行期间不可修改,常量可以是基本数据类型,如整数、浮点数、字符等,也可是自定义的数据类型;2、函数参数中的const修饰符,const关键字可用于函数的参数中,表示该参数在函数内部不可修改等等。

520

2023.09.20

js正则表达式
js正则表达式

php中文网为大家提供各种js正则表达式语法大全以及各种js正则表达式使用的方法,还有更多js正则表达式的相关文章、相关下载、相关课程,供大家免费下载体验。

508

2023.06.20

js获取当前时间
js获取当前时间

JS全称JavaScript,是一种具有函数优先的轻量级,解释型或即时编译型的编程语言;它是一种属于网络的高级脚本语言,主要用于Web,常用来为网页添加各式各样的动态功能。js怎么获取当前时间呢?php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

241

2023.07.28

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

251

2023.08.03

js是什么意思
js是什么意思

JS是JavaScript的缩写,它是一种广泛应用于网页开发的脚本语言。JavaScript是一种解释性的、基于对象和事件驱动的编程语言,通常用于为网页增加交互性和动态性。它可以在网页上实现复杂的功能和效果,如表单验证、页面元素操作、动画效果、数据交互等。

5234

2023.08.17

js删除节点的方法
js删除节点的方法

js删除节点的方法有:1、removeChild()方法,用于从父节点中移除指定的子节点,它需要两个参数,第一个参数是要删除的子节点,第二个参数是父节点;2、parentNode.removeChild()方法,可以直接通过父节点调用来删除子节点;3、remove()方法,可以直接删除节点,而无需指定父节点;4、innerHTML属性,用于删除节点的内容。

470

2023.09.01

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

206

2023.09.04

Js中concat和push的区别
Js中concat和push的区别

Js中concat和push的区别:1、concat用于将两个或多个数组合并成一个新数组,并返回这个新数组,而push用于向数组的末尾添加一个或多个元素,并返回修改后的数组的新长度;2、concat不会修改原始数组,是创建新的数组,而push会修改原数组,将新元素添加到原数组的末尾等等。本专题为大家提供concat和push相关的文章、下载、课程内容,供大家免费下载体验。

217

2023.09.14

php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

150

2025.12.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
React 教程
React 教程

共58课时 | 3.2万人学习

国外Web开发全栈课程全集
国外Web开发全栈课程全集

共12课时 | 0.9万人学习

React核心原理新老生命周期精讲
React核心原理新老生命周期精讲

共12课时 | 1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号