开玩笑如何允许模块的变异?

时间:2019-01-25 02:58:03

标签: jestjs es6-modules transpiler babel-jest

在这里我问的这个问题:

Why does mutating a module update the reference if calling that module from another module, but not if calling from itself?

我在问模块突变的性质。

事实证明,ES6模块实际上无法进行突变-它们的所有属性都被视为常量。 (See this answer

但是以某种方式-当Jest测试模块时-它们可以被突变,这就是Jest允许模拟的方式。

这是怎么回事?

我想象它正在运行的babel插件-将模块转换为CommonJS模块?是否有任何相关文档?

有没有办法查看转译的代码?

1 个答案:

答案 0 :(得分:6)

  

ES6模块实际上不能被突变-它们的所有属性都被视为常量。

有趣。您是对的,甚至像这样简单的事情:

import * as lib from "./lib";  // import an ES6 module
const spy = jest.spyOn(lib, 'someFunc');  // spy on someFunc

...从技术上讲,这是不允许的,因为jest.spyOn replaces the method on the object with a spylib.someFunc应该是ES6模块中与someFunc的绑定。


  

但是以某种方式-当Jest测试模块时-它们可以被突变,这就是Jest允许模拟的方式。

     

这是怎么回事?

只能更改它们,因为Jest实际上并未使用ES6模块。

(出于完整性考虑,可能可以通过使用Jest的{​​{3}}使用实际的ES6模块运行Node,但是我没有尝试过)。


  

我想象这是一个正在运行的babel插件-编译模块...关于此的任何文档吗?

experimental support for ES6 Modules

因此,默认情况下,Jest将使用babel-jest,后者会使用babel来转译源代码(并进行其他一些操作,例如"babel-jest is automatically installed when installing Jest and will automatically transform files if a babel configuration exists in your project. To avoid this behavior, you can explicitly reset the transform configuration option")。

请注意,Jest也可以使用hoisting calls to jest.mock进行配置,该映射将“正则表达式映射到转换器的路径”。


  

有没有办法查看转译的代码?

是的。转换是在jest-runtime transform中完成的,并将输出保存到缓存here

查看已编译代码的最简单方法是查看缓存。

您可以通过使用here选项运行Jest来执行此操作,该选项将输出运行config时使用的Jest。通过查看“ cacheDirectory”的值可以找到缓存位置。

然后使用--showConfig选项运行Jest以清除缓存。

最后,正常运行Jest,缓存目录将包含项目的已转码代码。


示例

最新的Jest(v24)将转换此代码:

// lib.js
export const someFunc = () => 1;


// code.js
import { someFunc } from './lib';
export const func = () => someFunc() + 1;


// code.test.js
import { func } from './code';
import * as lib from './lib';

test('func', () => {
  const spy = jest.spyOn(lib, 'someFunc');
  func();
  expect(spy).toHaveBeenCalled();  // SUCCESS
});

...对此:

// lib.js
"use strict";
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.someFunc = void 0;
const someFunc = () => 1;
exports.someFunc = someFunc;


// code.js
"use strict";
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.func = void 0;
var _lib = require("./lib");
const func = () => (0, _lib.someFunc)() + 1;
exports.func = func;


// code.test.js
"use strict";
var _code = require("./code");
var lib = _interopRequireWildcard(require("./lib"));
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }
test('func', () => {
  const spy = jest.spyOn(lib, 'someFunc');
  (0, _code.func)();
  expect(spy).toHaveBeenCalled(); // SUCCESS
});

import * as lib from 'lib';行由_interopRequireWildcard处理,该行在幕后使用require

每次调用require --clearCache时,code.jscode.test.js都会从require('./lib')获取相同的对象。

someFunc导出为exports.someFunc,可以重新分配。


是的,你是完全正确的。这样的间谍活动(或嘲笑)仅能正常工作,因为babel将ES6模块以允许它们突变的方式转换为Node模块。