jest 是如何 mock 掉模块的

本文介绍了Jest中模块Mock的实现方式,包括如何使用jest.mock来替换Node.js内置的fs模块,以及Jest背后对代码进行转换和注入自定义require实现的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

jest 里有两种 mock,一种是方法的 mock,还有一种是模块的 mock。这里我们来看一下模块的 mock 是如何实现的。

比如我们要 mock 掉 node 内置的 fs 模块,我们只要这么写:

const fs = require('fs');

jest.mock('fs');

console.log(fs.statSync('/tmp/file')); // undefined

jest 在执行这个文件的时候,首先会对代码进行转换,转换分成两步。

第一步是提升 jest.mock('fs'),让它能作用在 require 之前,转换后的代码如下:

jest.mock('fs');

const fs = require('fs');

jest.mock('fs');

console.log(fs.statSync('/tmp/file'));

第二部是包一层匿名方法,这一步跟 node 的模块实现类似:

(function(module, exports, require, __dirname, __filename, global, jest){
  jest.mock('fs');

  const fs = require('fs');

  console.log(fs.statSync('/tmp/file'));
}))

代码转换完后,jest 需要注入自己的 require 实现,这个一步通过让转换后的代码在 vm 模块创建的新的上下文里执行,最终生成一个可以执行的匿名方法实现。

const vm = require('vm');

const code = '转换后的代码';

const script = new vm.Script(code);

const result = script.runInContext(context); // describe, it 等全局方法在这里注入

result.call(
  module,
  exports,
  require, // jest 自己的 require 实现,
  ...
);

最后,我们用伪代码来描述下 require 的实现:

const shouldMock = {};

function mock(moduleName) {
  // jest 会给每个模块生成一个 moduleId, 比如这里是 `node:fs:` 表示这是一个 node 模块
  const moduleId = getModuleId(moduleName);
  shouldMock[moduleId] = true;
}

// 这个就是 jest 给我们的代码注入的 require 方法
function requireModuleOrMock(moduleName) {
  if(shouldMock(moduleName)) {
    return requireMockModule(moduleName);
  } else {
    return requireModule(moduleName);
  }
}

function shouldMock(moduleName) {
  const moduleId = getModuleId(moduleName);
  return moduleId in shouldMock;
}

function requireMockModule(moduleName) {
  const moduleExports =  requireModule(moduleName);
  return Object.keys(moduleExports).reduce((mock, key) => {
    mock[key] = () => {}; // mock 的方法
    return mock;
  }, {})
}

function requireModule(moduleName) {
  return require(moduleName); // 这个是原始的 require
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值