Skip to content Skip to sidebar Skip to footer

Writing Tests For Javascript Module Using Webpack's Require.ensure Function

I am running mocha tests on my server, testing source scripts an isolated unit test manner. One of the scripts I am testing makes a call to Webpack's require.ensure function, which

Solution 1:

Ok, I finally have an answer for this after much research and deliberation.

I initially thought that I could solve this using some sort of IoC / DI strategy, but then I found the source code for Node JS's Module library which is responsible for loading modules. Looking at the source code you will notice that the 'require' function for modules (i.e. foo.js in my example) get created by the _compile function of NodeJs's module loader. It's internally scoped and I couldn't see an immediate mechanism by which to modify it.

I am not quite sure how or where Webpack is extending the created "require" instance, but I suspect it is with some black magic. I realised that I would need some help to do something of a similar nature, and didn't want to write a massive block of complicated code to do so.

Then I stumbled on rewire...

Dependency injection for node.js applications.

rewire adds a special setter and getter to modules so you can modify their behaviour for better unit testing. You may

  • inject mocks for other modules
  • leak private variables
  • override variables within the module.
  • rewire does not load the file and eval the contents to emulate node's require mechanism. In fact it uses node's own require to load the module. Thus your module behaves exactly the same in your test environment as under regular circumstances (except your modifications).

Perfect. Access to private variables is all that I need.

After installing rewire, getting my test to work was easy:

foo.js

exportdefault {
  requireEnsureExists: () => {
    returntypeofrequire.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.... so happy. Fast running test land again.

Oh, in my case it's not needed, but if you are bundling your code via webpack for browser based testing, then you may need the rewire-webpack plugin. I also read somewhere that this may have problems with ES6 syntax.

Another note: for straight up mocking of require(...) statements I would recommend using mockery instead of rewire. It's less powerful than rewire (no private variable access), but this is a bit safer in my opinion. Also, it has a very helpful warning system to help you not do any unintentional mocking.


Update

I've also seen the following strategy being employed. In every module that uses require.ensure check that it exists and polyfill it if not:

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

Post a Comment for "Writing Tests For Javascript Module Using Webpack's Require.ensure Function"