指定在任何Jest安装发生之前运行的代码

时间:2017-09-26 18:45:05

标签: javascript jestjs

tl; dr是:

1)我如何让Jest使用本地require函数在我的测试中加载所有模块。

2)在任何测试运行之前,我在哪里/如何修改(即用esm loader替换)https://github.com/standard-things/esm require函数,所以所有测试都将使用修改后的require。

我想将esm-loader与我的Jest测试文件一起使用。为了做到这一点,我需要在任何测试代码运行之前全局修补require函数,例如

require = require("@std/esm")(module, { esm: "js", cjs: true });

在触摸或请求其他任何内容之前,如何告诉Jest执行该代码?

我尝试将setupTestFrameworkScriptFilesetupFiles数组条目指向包含该文件的文件,但都没有工作(尽管我确认两者都运行了)。

或者,我使用npm脚本启动这些测试

"scripts": {
  "test": "jest"
}

是否有一些CLI魔术我可以加载模块然后运行jest

编辑 - testEnvironmentresolver选项让我想知道是否甚至使用实际的Node require函数来加载模块,或者使用自己的模块加载器。如果是这样,我想知道这是否可行。

3 个答案:

答案 0 :(得分:16)

所以这个有点难以开始工作。解决方案非常简单,但我需要一段时间才能使其正常运行。问题是每当你在开玩笑中使用任何模块时

  • 设置文件
  • 设置框架文件
  • 测试文件
  • 模块文件

它们都是以下面的方式加载的

  

({“Object。”:function(module,exports,require,__ dirname,__ filename,global,jest){ / * *内的模块代码* /   }});

如果您查看node_modules/jest-runtime/build/index.js:495:510

const dirname = (_path || _load_path()).default.dirname(filename);
localModule.children = [];
localModule.parent = mockParentModule;
localModule.paths = this._resolver.getModulePaths(dirname);
localModule.require = this._createRequireImplementation(filename, options);

const transformedFile = this._scriptTransformer.transform(
filename,
{
  collectCoverage: this._coverageOptions.collectCoverage,
  collectCoverageFrom: this._coverageOptions.collectCoverageFrom,
  collectCoverageOnlyFrom: this._coverageOptions.collectCoverageOnlyFrom,
  isInternalModule,
  mapCoverage: this._coverageOptions.mapCoverage },

this._cacheFS[filename]);

this._createRequireImplementation(filename, options);为每个模块提供自定义需求对象。因此,您根本无法在任何地方获得本机需求功能。一旦jest启动,从那时起加载的每个模块都将具有jest的自定义require功能。

加载模块时,会调用requireModule中的jest-runtime方法。以下是相同的摘录

  moduleRegistry[modulePath] = localModule;
  if ((_path || _load_path()).default.extname(modulePath) === '.json') {
    localModule.exports = this._environment.global.JSON.parse(
    (0, (_stripBom || _load_stripBom()).default)((_gracefulFs || _load_gracefulFs()).default.readFileSync(modulePath, 'utf8')));

  } else if ((_path || _load_path()).default.extname(modulePath) === '.node') {
    // $FlowFixMe
    localModule.exports = require(modulePath);
  } else {
    this._execModule(localModule, options);
  }

如您所见,文件的扩展名为.node,它会直接加载模块,否则会调用_execModule。这个函数和我之前发布的代码转换代码相同

const isInternalModule = !!(options && options.isInternalModule);
const filename = localModule.filename;
const lastExecutingModulePath = this._currentlyExecutingModulePath;
this._currentlyExecutingModulePath = filename;
const origCurrExecutingManualMock = this._isCurrentlyExecutingManualMock;
this._isCurrentlyExecutingManualMock = filename;

const dirname = (_path || _load_path()).default.dirname(filename);
localModule.children = [];
localModule.parent = mockParentModule;
localModule.paths = this._resolver.getModulePaths(dirname);
localModule.require = this._createRequireImplementation(filename, options);

现在,当我们要为测试修改require函数时,我们需要_execModule直接导出我们的代码。所以代码应该类似于加载.node模块

  } else if ((_path || _load_path()).default.extname(modulePath) === '.mjs') {
    // $FlowFixMe
    require = require("@std/esm")(localModule);
    localModule.exports = require(modulePath);
  } else {

但这样做意味着修补我们想要避免的代码。所以我们做的是避免直接使用jest命令,并创建我们自己的jestload.js并运行它。加载jest的代码很简单

#!/usr/bin/env node
/**
 * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

cli = require('jest/bin/jest');

现在我们要在cli加载之前修改_execModule。所以我们添加下面的代码

const jestRuntime = require("jest-runtime");
oldexecModule = jestRuntime.prototype._execModule;

jestRuntime.prototype._execModule = function (localModule, options) {
    if (localModule.id.indexOf(".mjs") > 0) {
        localModule.exports = require("@std/esm")(localModule)(localModule.id);
        return localModule;
    }
    return oldexecModule.apply(this, [localModule, options]);
};

cli = require('jest/bin/jest');

现在是时候进行测试了

//__test__/sum.test.js
sum = require('../sum.mjs').sum;


test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3);
});


test('adds 2 + 3 to equal 5', () => {
  expect(sum(3, 2)).toBe(5);
});

sum.mjs文件

export function sum (x, y) { return x + y }

现在我们运行测试

Jest Test

解决方案可在以下repo

上找到

https://github.com/tarunlalwani/jest-overriding-require-function-stackoverflow

您可以通过运行npm test来克隆和测试解决方案。

答案 1 :(得分:-1)

setupFiles为我工作。将此添加到package.json中:

"jest": {
    "setupFiles": ["./my_file.js"]
  },

https://jestjs.io/docs/en/configuration.html#setupfiles-array

答案 2 :(得分:-2)

我尝试使用node -r @std/esm run.js,其中run.js只是一个调用jest的脚本,但它不起作用并在此崩溃:https://github.com/facebook/jest/blob/master/packages/jest-runtime/src/script_transformer.js#L305

根据我从这一行的理解,这意味着它是不可能的,因为jest使用本机vm模块编译模块。以上行(290):

  if (willTransform) {
    const transformedSource = this.transformSource(
    filename,
    content,
    instrument,
    !!(options && options.mapCoverage));


    wrappedCode = wrap(transformedSource.code);
    sourceMapPath = transformedSource.sourceMapPath;
  } else {

是在jest配置中指定转换时调用的代码。

结论:在支持esm之前(并且它们将在.mjs扩展名下),您无法在没有指定转换的情况下以jest 导入es模块。您可以尝试使用补丁vm,但我真的建议不要使用此选项。

指定一个jest变换真的不是那么难,对于es模块来说,它就像使用正确的babel配置babel-jest一样简单:

在具有最小设置的package.json下面

{
    "dependencies": {
        "babel-jest": "^21.2.0",
        "babel-plugin-transform-es2015-modules-commonjs": "^6.26.0",
        "jest": "^21.2.1"
    },
    "jest": {
        "testMatch": [
            "<rootDir>/src/**/__tests__/**/*.js?(x)",
            "<rootDir>/src/**/?(*.)(spec|test).js?(x)"
        ],
        "transform": {
            "^.+\\.(js|jsx)$": "<rootDir>/node_modules/babel-jest"
        },
        "testEnvironment": "node",
        "testURL": "http://localhost",
        "moduleFileExtensions": [
            "js",
            "json"
        ]
    },
    "babel": {
        "plugins": ["babel-plugin-transform-es2015-modules-commonjs"]
    }
}