【问题标题】:Specify code to run before any Jest setup happens指定在任何 Jest 设置发生之前运行的代码
【发布时间】:2018-03-08 02:29:26
【问题描述】:

tl;dr 是:

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

2) 在任何测试运行之前,我将在哪里/如何修改(即替换为 esm 加载程序)https://github.com/standard-things/esm 一个地方的 require 函数,因此所有测试都将使用修改后的 require。


我想在我的 Jest 测试文件中使用 esm-loader。为此,我需要在运行任何测试代码之前全局修补 require 函数,例如

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

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

我尝试将 setupTestFrameworkScriptFilesetupFiles 数组条目都指向其中包含该文件的文件,但均未成功(尽管我确实确认两者都运行了)。

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

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

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


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

【问题讨论】:

  • @OrB - 谢谢,但遗憾的是没有。我需要的是较低级别的。你想用一个支持 ES6 的垫片替换根节点需要加载器。但鉴于我的编辑,我什至不确定 Node 是否使用根节点解析器,所以这甚至不可能
  • 从你的 npm 脚本使用来看,jest-cli 应该是第一个运行的东西。您可以尝试在本地编辑此文件,在 require('../build/cli').run(); 之前使用 require("@std/esm") 修补 require 函数。如果这可行,您至少可以确认您的想法是正确的。
  • @BrandonBoone 好主意 - 我这样做了,但仍然没有任何变化(我肯定确认我有正确的文件)。似乎 Jest 根本不使用 Node 的 require 在测试中加载模块(考虑到它们的模块模拟​​能力,这是有道理的。)
  • 也许吧。 Jest 确实有 2 个包 jest-resolve-dependenciesjest-resolve 似乎正在协同工作以解析模块路径。我确实看起来你可以通过configuration 覆盖它

标签: javascript jestjs


【解决方案1】:

setupFiles 为我工作。在 package.json 中添加:

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

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

【讨论】:

  • my_file.js的内容是什么?
  • 任何你想在你的笑话文件之前运行的东西。就我而言,它正在添加一个 pollyfill: import 'raf/polyfill';
  • 对,但是该文件中究竟需要什么才能使 Jest 使用修改后的 require 加载 ES 模块?
  • 我不知道为什么它有两个反对票!另外我认为答案可能会从官方网站获得更多信息,例如“setupFiles 是运行一些代码以配置或设置测试环境的模块的路径列表。”。我会试试这个,似乎正是我想要的!谢谢!
【解决方案2】:

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

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

它们都以以下方式加载

({"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); 给每个模块一个自定义的 require 对象。因此,您根本无法在任何地方获得本机 require 功能。一旦 jest 启动,从那时起加载的每个模块都将具有 jest 的自定义 require 函数。

当我们加载一个模块时,来自jest-runtimerequireModule 方法被调用。以下是同一节的摘录

  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 }

现在我们运行测试

解决方案可在以下 repo 中找到

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

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

【讨论】:

  • 当我尝试使用节点 v10.5.0 run the test from the repo 时,我得到了 Unexpected token export
  • 你可以试试 Node 8 LTS 版本吗?这就是它所测试的
【解决方案3】:

我尝试使用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-jest 和正确的 babel 配置一样简单:

在具有最少设置的 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"]
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-07-01
    • 2012-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-21
    • 2017-05-02
    相关资源
    最近更新 更多