如何在Jest中模拟Webpack的require.context?

bor*_*ges 28 webpack jestjs

假设我有以下模块:

var modulesReq = require.context('.', false, /\.js$/);
modulesReq.keys().forEach(function(module) {
  modulesReq(module);
});
Run Code Online (Sandbox Code Playgroud)

Jest抱怨因为它不知道require.context:

 FAIL  /foo/bar.spec.js (0s)
? Runtime Error
  - TypeError: require.context is not a function
Run Code Online (Sandbox Code Playgroud)

我怎么嘲笑它?我尝试使用setupTestFrameworkScriptFileJest配置,但测试无法看到我所做的任何更改require.

Sat*_*hak 23

在花了几个小时尝试上述每个答案之后。我愿意做出贡献。

为测试环境添加babel-plugin-transform-require-context插件.babelrc修复了所有问题。

安装 -babel-plugin-transform-require-context在这里https://www.npmjs.com/package/babel-plugin-transform-require-context(也可用yarn

现在添加插件 .babelrc

{
  "env": {
    "test": {
      "plugins": ["transform-require-context"]
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

它将简单地将require-context测试环境转换为虚拟fn调用,以便代码可以安全运行。

  • 未来的读者请注意:这本身并不能解决问题,它只是通过解决空上下文来解决问题。如果您的代码仍然需要 require.context 才能工作 - 这个答案对您没有帮助。 (4认同)

Edm*_*ues 20

我遇到了同样的问题,然后我做了一个"解决方案".

我很确定这不是最好的选择.我最后停止使用它,通过这里回答的要点:

https://github.com/facebookincubator/create-react-app/issues/517 https://github.com/facebook/jest/issues/2298

但是如果你真的需要它,你应该将下面的polyfill包含在你调用它的每个文件中(而不是在测试文件本身上,因为require在Node环境中不会全局覆盖).

// This condition actually should detect if it's an Node environment
if (typeof require.context === 'undefined') {
  const fs = require('fs');
  const path = require('path');

  require.context = (base = '.', scanSubDirectories = false, regularExpression = /\.js$/) => {
    const files = {};

    function readDirectory(directory) {
      fs.readdirSync(directory).forEach((file) => {
        const fullPath = path.resolve(directory, file);

        if (fs.statSync(fullPath).isDirectory()) {
          if (scanSubDirectories) readDirectory(fullPath);

          return;
        }

        if (!regularExpression.test(fullPath)) return;

        files[fullPath] = true;
      });
    }

    readDirectory(path.resolve(__dirname, base));

    function Module(file) {
      return require(file);
    }

    Module.keys = () => Object.keys(files);

    return Module;
  };
}
Run Code Online (Sandbox Code Playgroud)

使用此函数,您不需要更改任何require.context调用,它将以与它相同的行为执行(如果它在webpack上,它将只使用原始实现,如果它在Jest执行中,则使用polyfill函数).

  • fs是NodeJS附带的本机包.您不需要安装它.它可以直接添加require('fs'),因为你的Jest测试在NodeJS环境中运行. (4认同)

bor*_*kur 15

将调用解压缩到单独的模块:

// src/js/lib/bundle-loader.js
/* istanbul ignore next */
module.exports = require.context('bundle-loader?lazy!../components/', false, /.*\.vue$/)
Run Code Online (Sandbox Code Playgroud)

在您从中解压缩的模块中使用新模块:

// src/js/lib/loader.js
const loadModule = require('lib/bundle-loader')
// ...
Run Code Online (Sandbox Code Playgroud)

为新创建的bundle-loader模块创建一个mock:

// test/unit/specs/__mocks__/lib/bundle-loader.js
export default () => () => 'foobar'
Run Code Online (Sandbox Code Playgroud)

在测试中使用mock:

// test/unit/specs/lib/loader.spec.js
jest.mock('lib/bundle-loader')
import Loader from 'lib/loader'

describe('lib/loader', () => {
  describe('Loader', () => {
    it('should load', () => {
      const loader = new Loader('[data-module]')
      expect(loader).toBeInstanceOf(Loader)
    })
  })
})
Run Code Online (Sandbox Code Playgroud)

  • @Bartekus如果您说这不是一个好的解决方案(或投反对票),请也请善意地解释一下,为什么您认为这不是一个好的解决方案。 (3认同)

小智 12

如果您使用的是Babel,请查看babel-plugin-require-context-hook。有关Storybook的配置说明,请参见Storyshots | 将Jest配置为可与Webpack的require.context()一起使用,但它们并非特定于Storyshots / Storybook。

总结一下:

安装插件。

yarn add babel-plugin-require-context-hook --dev
Run Code Online (Sandbox Code Playgroud)

创建一个.jest/register-context.js具有以下内容的文件:

import registerRequireContextHook from 'babel-plugin-require-context-hook/register';
registerRequireContextHook();
Run Code Online (Sandbox Code Playgroud)

配置Jest(文件取决于您存储Jest配置的位置,例如package.json):

setupFiles: ['<rootDir>/.jest/register-context.js']
Run Code Online (Sandbox Code Playgroud)

将插件添加到 .babelrc

{
  "presets": ["..."],
  "plugins": ["..."],
  "env": {
    "test": {
      "plugins": ["require-context-hook"]
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

或者,将其添加到babel.config.js

module.exports = function(api) {
  api.cache(true)

  const presets = [...]
  const plugins = [...]

  if (process.env.ENV_NODE === "test") {    
    plugins.push("require-context-hook")    
  }

  return {
    presets,
    plugins
  }
}
Run Code Online (Sandbox Code Playgroud)

可能值得注意的是,使用babel.config.js而不是.babelrc可能引起问题。例如,我发现当我在中定义require-context-hook插件时babel.config.js

  • 笑话22没捡到;
  • 笑话23捡起来;但
  • jest --coverage 没有拿起它(也许伊斯坦布尔不适应Babel 7?)。

在所有情况下,.babelrc配置都很好。


在备注埃德蒙多·罗德里格斯的答案

babel-plugin-require-context-hook插件使用的代码类似于Edmundo Rodrigues在此处的答案。埃德蒙多的道具!由于该插件是作为Babel插件实现的,因此可以避免静态分析问题。例如,使用Edmundo的解决方案,Webpack警告:

Critical dependency: require function is used in a way in which dependencies cannot be statically extracted
Run Code Online (Sandbox Code Playgroud)

尽管有警告,Edmundo的解决方案还是最可靠的,因为它不依赖Babel。


Pat*_*ard 9

好吧!我在这方面遇到了重大问题,并通过结合其他答案和文档设法找到了对我有用的解决方案。(不过我度过了愉快的一天)

对于其他正在挣扎的人:

创建一个名为bundle-loader.js 的文件并添加如下内容:

module.exports = {
  importFiles: () => {
    const r = require.context(<your_path_to_your_files>)
    <your_processing> 
    return <your_processed_files>
  }
}
Run Code Online (Sandbox Code Playgroud)

在您的代码中导入如下:

import bundleLoader from '<your_relative_Path>/bundle-loader'
Run Code Online (Sandbox Code Playgroud)

使用类似

let <your_var_name> = bundleLoader.importFiles()
Run Code Online (Sandbox Code Playgroud)

在您的测试文件中其他导入的正下方:

jest.mock('../../utils/bundle-loader', () => ({
  importFiles: () => {
    return <this_will_be_what_you_recieve_in_the_test_from_import_files>
  }
}))
Run Code Online (Sandbox Code Playgroud)


小智 6

安装

babel-plugin-transform-require-context

打包并在 .babelrc 中添加插件为我解决了这个问题。请参阅此处的文档:https : //www.npmjs.com/package/babel-plugin-transform-require-context


Bar*_*kus 5

解决这个问题最简单、最快的方法是安装require-context.macro

npm install --save-dev require-context.macro
Run Code Online (Sandbox Code Playgroud)

然后只需替换:

var modulesReq = require.context('.', false, /\.js$/);
Run Code Online (Sandbox Code Playgroud)

和:

var modulesReq = requireContext('.', false, /\.js$/);
Run Code Online (Sandbox Code Playgroud)

就是这样,你应该可以走了!干杯,祝你好运!