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

ctr*_*usb 1 javascript unit-testing mocha.js node.js webpack

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

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

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

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

有一个包,webpack-require,它允许创建webpack上下文.这可行,但速度慢得令人无法接受.我希望有一种require.ensure直接针对该功能的轻量级polyfill .

有什么建议?:)


这是一个非常基本的起点摩卡测试.

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

foo.js

export default {
  requireEnsureExists: () => {
    return typeof require.ensure === 'function';
  }
};
Run Code Online (Sandbox Code Playgroud)

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;
  });
});
Run Code Online (Sandbox Code Playgroud)

ctr*_*usb 5

好的,经过大量的研究和考虑,我终于得到了答案.

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

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

然后我偶然发现了重新布线 ......

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

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

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

完善.访问私有变量就是我所需要的.

安装重新布线后,让我的测试工作很简单:

foo.js

export default {
  requireEnsureExists: () => {
    return typeof require.ensure === 'function';
  }
};
Run Code Online (Sandbox Code Playgroud)

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;
  });
});
Run Code Online (Sandbox Code Playgroud)

Aaaaah ......太开心了.快速运行测试土地了.

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

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


更新

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

// Polyfill webpack require.ensure.
if (typeof require.ensure !== `function`) require.ensure = (d, c) => c(require);    
Run Code Online (Sandbox Code Playgroud)