使用webpack的require.ensure函数

时间:2015-10-11 21:50:07

标签: javascript node.js unit-testing mocha webpack

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

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

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

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

有一个包webpack-require,它允许创建webpack上下文。这可行,但速度慢得令人无法接受。我希望直接针对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;
  });
});

1 个答案:

答案 0 :(得分:5)

好的,经过大量的研究和审议,我终于得到了答案。

我最初认为我可以使用某种IoC / DI策略解决这个问题,但后来我找到负责加载模块的source code for Node JS's Module library。查看源代码,您会发现'要求'模块的函数(即我的例子中的foo.js)由_compile function of NodeJs's module loader创建。它是内部范围的,我无法看到修改它的直接机制。

我不太确定Webpack是如何或在何处扩展已创建的"要求"例如,但我怀疑它有一些黑魔法。我意识到我需要一些帮助来做一些类似的事情,并且不想编写大量复杂的代码来做这件事。

然后我偶然发现rewire ......

  

node.js应用程序的依赖注入。

     

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

     
      
  • 为其他模块注入模拟
  •   
  • 泄漏私人变量
  •   
  • 覆盖模块中的变量。
  •   
  • 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语法有关。

另一个注意事项:对于直接模拟require(...)语句,我建议使用mockery而不是重新连接。它没有重新连接那么强大(没有私有变量访问),但在我看来这有点安全。此外,它有一个非常有用的警告系统,可以帮助你不要做任何无意的嘲笑。

<强>更新

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

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