使用 webpack 的 require.sure 函数为 javascript 模块编写测试



我正在服务器上运行mocha测试,以独立的单元测试方式测试源脚本。

我正在测试的一个脚本调用Webpack的require.ensure函数,该函数在Webpack捆绑应用程序时用于在应用程序中创建代码分割点。

我为这个脚本编写的测试没有在Webpack上下文中运行,因此require.ensure函数不存在,测试失败。

我试着为这个函数创建一些polyfill/stub/mock/spy,但是没有任何运气。

有一个包,webpack-require,它允许创建一个webpack上下文。这是可行的,但速度慢得令人无法接受。我更愿意有某种轻量级的polyfill直接针对require.ensure函数。

建议吗?:)


这是一个非常基本的摩卡测试。

mocha测试加载一个人造模块,该模块包含一个方法,如果定义了require.ensure,则返回true。

foo.js

export default {
  requireEnsureExists: () => {
    return typeof require.ensure === 'function';
  }
};

foo.test.js

import { expect } from 'chai';
describe('When requiring "foo"', () => {
  let foo;
  before(() => {
    foo = require('./foo.js');
  });
  it('The requireEnsureExists() should be true', () => {
    expect(foo.requireEnsureExists()).to.be.true;
  });
});

好了,经过一番研究和思考,我终于有了答案。

我最初认为我可以使用某种IoC/DI策略来解决这个问题,但后来我找到了Node JS的模块库的源代码,该库负责加载模块。查看源代码,你会注意到模块的'require'函数(即在我的示例中为foo.js)由NodeJs的模块加载器的_compile函数创建。它的作用域是内部的,我看不到一个可以立即修改它的机制。

我不太确定Webpack是如何或在哪里扩展创建的"require"实例的,但我怀疑它是用一些黑魔法。我意识到我需要一些帮助来做类似的事情,而不想写一大块复杂的代码来做这件事。

然后我偶然发现了rewire…

node.js应用的依赖注入。

rewire为模块添加了一个特殊的setter和getter,这样你就可以修改它们的行为以进行更好的单元测试。你可能

  • 为其他模块注入mock
  • 泄漏私有变量
  • 覆盖模块内的变量。
  • rewire不加载文件和计算内容来模拟节点的需求机制。实际上,它使用node自己的require来加载模块。因此,您的模块在测试环境中的行为与常规情况下完全相同(除了您的修改)。

完美。我只需要访问私有变量。

安装rewire后,让我的测试工作很容易:

foo.js

export default {
  requireEnsureExists: () => {
    return typeof require.ensure === 'function';
  }
};

foo.test.js

import { expect } from 'chai';
import rewire from 'rewire';
describe('When requiring "foo"', () => {
  let foo;
  before(() => {
    foo = rewire('./foo.js');
    // Get the existing 'require' instance for our module.
    let fooRequire = moduletest.__get__('require');
    // Add an 'ensure' property to it.
    fooRequire.ensure = (path) => {
      // Do mocky/stubby stuff here.
    };
    // We don't need to set the 'require' again in our module, as the above
    // is by reference.
  });
  it('The requireEnsureExists() should be true', () => {
    expect(foo.requireEnsureExists()).to.be.true;
  });
});

Aaaaah……如此高兴的原因。再次快速试车。

哦,在我的情况下,它是不需要的,但如果你通过webpack捆绑你的代码进行基于浏览器的测试,那么你可能需要rewire-webpack插件。我还在某处读到,这可能与ES6的语法有问题。

另一个注意事项:对于直接mock require(…)语句,我建议使用mock而不是rewire。它不如rewire(没有私有变量访问)强大,但在我看来这更安全一些。此外,它有一个非常有用的警告系统,以帮助您避免任何无意的嘲弄。


我还看到下面的策略被采用。在每个使用require.ensure的模块中检查它是否存在,如果不存在则填充它:

// Polyfill webpack require.ensure.
if (typeof require.ensure !== `function`) require.ensure = (d, c) => c(require);    

最新更新