开玩笑如何允许模块突变?



在我在这里问的这个问题中:

如果从另一个模块调用该模块,为什么更改模块会更新引用,但如果从自身调用,则不会更新引用?

我问的是模块突变的性质。

然而,事实证明,ES6 模块实际上不能变异 - 它们的所有属性都被视为常量。(见此答案)

但不知何故 - 当 Jest 测试模块时 - 它们可以发生变异,这就是 Jest 允许嘲笑的方式。

这是怎么回事?

我想这是一个正在运行的 babel 插件 - 将模块转换为 CommonJS 模块?有关于此的文档吗?

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

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

有趣。 你是对的,即使是这样简单的事情:

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

。从技术上讲,不应该允许,因为jest.spyOn用间谍替换对象上的方法,lib.someFunc应该是 ES6 模块中someFunc的绑定。


但不知何故 - 当 Jest 测试模块时 - 它们可以发生突变,这就是 Jest 允许嘲笑的方式。

这是怎么回事?

它们只能变异,因为Jest实际上并没有使用 ES6 模块。

(我想为了完整起见,通过使用Node对 ES6 模块的实验性支持,可以使用实际的 ES6 模块运行Jest,但我还没有尝试过)。


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

"babel-jest在安装 Jest 时自动安装,如果您的项目中存在 babel 配置,则会自动转换文件。 要避免此行为,您可以显式重置transform配置选项"。

因此,默认情况下,Jest将使用babel-jest,它使用babel转译源代码(并执行其他一些操作,例如提升对jest.mock的调用)。

请注意,也可以使用将"正则表达式映射到转换器路径"的transform来配置Jest


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

是的。 转换在此处以jest-runtime完成,输出在此处保存到缓存中。

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

您可以通过使用--showConfig选项运行Jest来做到这一点,该选项将输出运行Jest时使用的config。 可以通过查看"cacheDirectory"的值找到缓存位置。

然后使用--clearCache选项运行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"将返回完全相同的对象,如果它将解析为相同的文件",因此code.jscode.test.jsrequire('./lib')获取相同的对象。

someFunc导出为exports.someFunc,允许重新分配。


所以是的,你说得很对。 像这样的间谍(或嘲笑)之所以有效,是因为 ES6 模块被babel以一种允许它们变异的方式转译成Node模块。

最新更新