Git Product home page Git Product logo

rewire's Introduction

rewire

Easy monkey-patching for node.js unit tests

Dependency Status Build Status Coverage Status

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 or globals like process
  • inspect private variables
  • override variables within the module.

Please note: The current version of rewire is only compatible with CommonJS modules. See Limitations.


Installation

npm install rewire


Introduction

Imagine you want to test this module:

// lib/myModule.js
// With rewire you can change all these variables
var fs = require("fs"),
    path = "/somewhere/on/the/disk";

function readSomethingFromFileSystem(cb) {
    console.log("Reading from file system ...");
    fs.readFile(path, "utf8", cb);
}

exports.readSomethingFromFileSystem = readSomethingFromFileSystem;

Now within your test module:

// test/myModule.test.js
var rewire = require("rewire");

var myModule = rewire("../lib/myModule.js");

rewire acts exactly like require. With just one difference: Your module will now export a special setter and getter for private variables.

myModule.__set__("path", "/dev/null");
myModule.__get__("path"); // = '/dev/null'

This allows you to mock everything in the top-level scope of the module, like the fs module for example. Just pass the variable name as first parameter and your mock as second.

var fsMock = {
    readFile: function (path, encoding, cb) {
        expect(path).to.equal("/somewhere/on/the/disk");
        cb(null, "Success!");
    }
};
myModule.__set__("fs", fsMock);

myModule.readSomethingFromFileSystem(function (err, data) {
    console.log(data); // = Success!
});

You can also set multiple variables with one call.

myModule.__set__({
    fs: fsMock,
    path: "/dev/null"
});

You may also override globals. These changes are only within the module, so you don't have to be concerned that other modules are influenced by your mock.

myModule.__set__({
    console: {
        log: function () { /* be quiet */ }
    },
    process: {
        argv: ["testArg1", "testArg2"]
    }
});

__set__ returns a function which reverts the changes introduced by this particular __set__ call

var revert = myModule.__set__("port", 3000);

// port is now 3000
revert();
// port is now the previous value

For your convenience you can also use the __with__ method which reverts the given changes after it finished.

myModule.__with__({
    port: 3000
})(function () {
    // within this function port is 3000
});
// now port is the previous value again

The __with__ method is also aware of promises. If a thenable is returned all changes stay until the promise has either been resolved or rejected.

myModule.__with__({
    port: 3000
})(function () {
    return new Promise(...);
}).then(function () {
    // now port is the previous value again
});
// port is still 3000 here because the promise hasn't been resolved yet

Limitations

Babel's ES module emulation
During the transpilation step from ESM to CJS modules, Babel renames internal variables. Rewire will not work in these cases (see #62). Other Babel transforms, however, should be fine. Another solution might be switching to babel-plugin-rewire.

Variables inside functions
Variables inside functions can not be changed by rewire. This is constrained by the language.

// myModule.js
(function () {
    // Can't be changed by rewire
    var someVariable;
})()

Modules that export primitives
rewire is not able to attach the __set__- and __get__-method if your module is just exporting a primitive. Rewiring does not work in this case.

// Will throw an error if it's loaded with rewire()
module.exports = 2;

Globals with invalid variable names
rewire imports global variables into the local scope by prepending a list of var declarations:

var someGlobalVar = global.someGlobalVar;

If someGlobalVar is not a valid variable name, rewire just ignores it. In this case you're not able to override the global variable locally.

Special globals
Please be aware that you can't rewire eval() or the global object itself.


API

rewire(filename: String): rewiredModule

Returns a rewired version of the module found at filename. Use rewire() exactly like require().

rewiredModule.__set__(name: String, value: *): Function

Sets the internal variable name to the given value. Returns a function which can be called to revert the change.

rewiredModule.__set__(obj: Object): Function

Takes all enumerable keys of obj as variable names and sets the values respectively. Returns a function which can be called to revert the change.

rewiredModule.__get__(name: String): *

Returns the private variable with the given name.

rewiredModule.__with__(obj: Object): Function<callback: Function>

Returns a function which - when being called - sets obj, executes the given callback and reverts obj. If callback returns a promise, obj is only reverted after the promise has been resolved or rejected. For your convenience the returned function passes the received promise through.


Caveats

Difference to require()
Every call of rewire() executes the module again and returns a fresh instance.

rewire("./myModule.js") === rewire("./myModule.js"); // = false

This can especially be a problem if the module is not idempotent like mongoose models.

Globals are imported into the module's scope at the time of rewiring
Since rewire imports all gobals into the module's scope at the time of rewiring, property changes on the global object after that are not recognized anymore. This is a problem when using sinon's fake timers after you've called rewire().

Dot notation
Although it is possible to use dot notation when calling __set__, it is strongly discouraged in most cases. For instance, writing myModule.__set__("console.log", fn) is effectively the same as just writing console.log = fn. It would be better to write:

myModule.__set__("console", {
    log: function () {}
});

This replaces console just inside myModule. That is, because rewire is using eval() to turn the key expression into an assignment. Hence, calling myModule.__set__("console.log", fn) modifies the log function on the global console object.


webpack

See rewire-webpack


License

MIT

rewire's People

Contributors

2pacalypse- avatar bobpace avatar breautek avatar bttmly avatar dhensby avatar emilgoldsmith avatar jhnns avatar jvjefke avatar mcandre avatar nisaacson avatar odedniv avatar pdehaan avatar rensbaardman avatar sokra avatar stevemao avatar suisho avatar tfitos avatar thesavior avatar trodrigues avatar trott avatar tschaub avatar tsekityam avatar vkarpov15 avatar zolmeister avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rewire's Issues

reverting a JSON rewire fails

The following test passes if I use 2.1.5, but fails with 2.3. I'm unsure if i'm doing something wrong or not.

var assert = require("assert");
var should = require('should');
var rewire = require('rewire');
var sinon = require('sinon');

var user = rewire('../user');

describe('user', function(){
    describe('#buildNumber', function(){
        it('should return the value contained in the field "build"', function(){
            var rewireRevert = user.__set__({JSON : {
            "parse": function()
            {
                return {"build":"4"};
            }}});

            var buildNumber = user.buildNumber()
            should(buildNumber).be.ok;
            buildNumber.should.be.eql("4");

            //reset the user object to its original state
            rewireRevert();
            })
        it('should not fail when trying to get the version from the package.json', function(){
            var buildNumber = user.buildNumber()
            should(buildNumber).be.ok;
        })
    })
})

The code being tested:

var http = require('http');
var util = require('util');
var fs = require('fs');

function buildNumber()
{
    var packageVersion = JSON.parse(fs.readFileSync(__dirname + '/package.json', 'utf8')).build;

    //extract the "major" version
    return packageVersion;
}

The error returned:

user #buildNumber should return the value contained in the field "build":
     TypeError: undefined is not a function
      at __set__ (/Users/otusweb/Documents/elektra-liveshare/user.js:149:81)
      at /Users/otusweb/Documents/elektra-liveshare/user.js:178:17
      at Context.<anonymous> (/Users/otusweb/Documents/elektra-liveshare/test/user.test.js:22:4)
      at callFn (/usr/local/lib/node_modules/mocha/lib/runnable.js:251:21)
      at Test.Runnable.run (/usr/local/lib/node_modules/mocha/lib/runnable.js:244:7)
      at Runner.runTest (/usr/local/lib/node_modules/mocha/lib/runner.js:374:10)
      at /usr/local/lib/node_modules/mocha/lib/runner.js:452:12
      at next (/usr/local/lib/node_modules/mocha/lib/runner.js:299:14)
      at /usr/local/lib/node_modules/mocha/lib/runner.js:309:7
      at next (/usr/local/lib/node_modules/mocha/lib/runner.js:248:23)
      at Immediate._onImmediate (/usr/local/lib/node_modules/mocha/lib/runner.js:276:5)
      at processImmediate [as _immediateCallback] (timers.js:358:17)

Skip Some Modules

If you don't mutate some modules, would it be possible for them to keep the same instance?

TypeError: Cannot read property '0' of undefined

I'm using karma-runner to run jasmine tests, with the setup below.

I'm getting this error when launching karma:

Chrome 41.0.2272 (Linux) ERROR
Uncaught TypeError: Cannot read property '0' of undefined  at tmp/cc96bc0f13c8df482b64e5d6150bb474.browserify:27303:0 <- ../../../../../../node_modules/rewire/lib/moduleEnv.js:7:0

karma.conf.js: https://gist.github.com/phun-ky/8dbc66b211c718252e58

Rewire module from http://substantial.com/blog/2014/11/11/test-driven-react-how-to-manually-mock-components/ :
var rewireModule = function rewireModule(rewiredModule, varValues) {
var rewiredReverts = [];

  beforeEach(function() {
    var key, value, revert;
    for (key in varValues) {
      if (varValues.hasOwnProperty(key)) {
        value = varValues[key];
        revert = rewiredModule.__set__(key, value);
        rewiredReverts.push(revert);
      }
    }
  });

  afterEach(function() {
    rewiredReverts.forEach(function(revert) {
      revert();
    });
  });

  return rewiredModule;
};

module.exports = rewireModule;

Spec used:

/**
  @see http://substantial.com/blog/2014/11/11/test-driven-react-how-to-manually-mock-components/
*/
var React = require("react/addons");
var TestUtils = React.addons.TestUtils;
var rewire = require("rewire");
var rewireModule = require("../../rewire-module.js");

describe('MainComponent', function() {
  var component;

  // `rewire` instead of `require`
  var MainComponent = rewire("../components/MainComponent.react.js");

  // Replace the required module with a stub component.
  rewireModule(MainComponent, {
    ComponentA: React.createClass({
      render: function() { return <div />; }
    }),
    ComponentB: React.createClass({
      render: function() { return <div />; }
    }),
    ComponentC: React.createClass({
      render: function() { return <div />; }
    }),
    ComponentD: React.createClass({
      render: function() { return <div />; }
    })
  });

  // Render the component from the rewired module.
  beforeEach(function() {
    component = TestUtils.renderIntoDocument( <MainComponent /> );
  });

  it("renders", function() {
    var foundComponent = TestUtils.findRenderedDOMComponentWithClass(
      component, 'MainComponent');

    expect(foundComponent).toBeDefined();
  });
});

Using these node modules:

"bootstrap": "^3.3.2",
"browserify": "^8.1.3",
"coffee-script": "^1.9.1",
"flux": "^2.0.1",
"gulp": "^3.8.11",
"gulp-bump": "^0.3.0",
"gulp-clean": "0.3.1",
"gulp-jasmine": "^2.0.0",
"gulp-jsdoc": "^0.1.4",
"gulp-jshint": "^1.9.2",
"gulp-less": "^3.0.0",
"gulp-minify-css": "^1.0.0",
"gulp-print": "1.1.0",
"gulp-react": "^3.0.1",
"gulp-rename": "1.2.0",
"gulp-sourcemaps": "^1.3.0",
"gulp-tap": "^0.1.3",
"gulp-uglify": "^1.1.0",
"gulp-util": "^3.0.3",
"gulp-verb": "^0.3.0",
"hammerjs": "^2.0.4",
"instafeed.js": "git+https://github.com/gunnarbergem/instafeed.js.git",
"jQuery.mmenu": "git+https://github.com/ganchenkor/jQuery.mmenu.git",
"jasmine-core": "^2.2.0",
"jquery": "^2.1.3",
"jquery-ui": "^1.10.5",
"jquery-ui-touch-punch": "^0.2.3",
"jsdoc-to-markdown": "^0.6.4",
"jshint-stylish": "^1.0.1",
"karma": "^0.12.31",
"karma-browserify": "^4.0.0",
"karma-chrome-launcher": "^0.1.7",
"karma-jasmine": "^0.3.5",
"karma-phantomjs-launcher": "^0.1.4",
"lodash": "^3.1.0",
"moment": "^2.9.0",
"object-assign": "^2.0.0",
"react": "^0.12.2",
"react-hammerjs": "^0.1.3",
"react-mini-router": "^1.0.0",
"react-tools": "^0.12.2",
"reactify": "^1.0.0",
"remarkable": "^1.6.0",
"rewire": "^2.3.1",
"underscore": "^1.7.0",
"vinyl-buffer": "^1.0.0",
"vinyl-source-stream": "^1.0.0",
"watchify": "^2.3.0"

NodeJS v0.12.0

Improve readme regarding caching caveats

rewire() adds all mocked modules to require.cache by default. While this is good in cases of circular dependencies it may be a problem when there are many different tests involved with the same module. This issue should be documented better in the readme to raise the developer's attention.

Object.defineProperty called on non-object

Hopefully this isn't just user error, since I'm new to rewire.

I am getting the following error when I change a require to a rewire:

TypeError: Object.defineProperty called on non-object
    at defineProperty (native)
    at Function.defineProperty (/mnt/sdb1/code/dev/sane/node_modules/traceur/bin/traceur.js:257:7)
    at Object.<anonymous> (/mnt/sdb1/code/dev/sane/lib/helpers/ember.js:29:8)
    at Module._compile (module.js:410:26)
    at Object.Module._extensions..js (/mnt/sdb1/code/dev/sane/node_modules/traceur/src/node/require.js:65:21)
    at Module.load (module.js:335:32)
    at Object.load (/mnt/sdb1/code/dev/sane/node_modules/rewire/lib/moduleEnv.js:19:18)
    at internalRewire (/mnt/sdb1/code/dev/sane/node_modules/rewire/lib/rewire.js:50:15)
    at rewire (/mnt/sdb1/code/dev/sane/node_modules/rewire/lib/index.js:11:12)
    at Object.<anonymous> (/mnt/sdb1/code/dev/sane/tests/unit/helpers/emberTest.js:4:13)

Where it seems to be failing in the module I'm requiring is here (ember.js:29:8):

  var globalPath = path.join(
      require.resolve('ember-cli')
      .replace(/\/lib\/cli\/index\.js$/, ''),
      'bin',
      'ember');

Any thoughts on what could be going wrong here? I can provide more info if you let me know what would be helpful. Thanks!

Mongoose model overwrite error

I've used rewire with a Mongoose model in a jasmine spec, and also used require to load the same model in a different spec. I get this error:

{ message: 'Cannot overwrite `Organisation` model once compiled.', name: 'OverwriteModelError' }

It looks like it's asking Mongoose to define the same model twice, whereas if we used require both times this does not happen.

Is there a workaround?

Thanks.

Some strange issue which can be possible linked with rewire module

I have stack trace:

SyntaxError: Unexpected token -
    at Module._compile (module.js:439:25)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Object.load (/Users/bemer/Work/builder-docs/node_modules/mock-fs/node_modules/rewire/lib/moduleEnv.js:19:18)
    at internalRewire (/Users/bemer/Work/builder-docs/node_modules/mock-fs/node_modules/rewire/lib/rewire.js:56:15)
    at rewire (/Users/bemer/Work/builder-docs/node_modules/mock-fs/node_modules/rewire/lib/index.js:11:12)
    at Object.<anonymous> (/Users/bemer/Work/builder-docs/node_modules/mock-fs/lib/index.js:41:14)

Can some last changes in rewire module cause this error?

rewire for an anonymous closure

I thought my problem should have been resolved by reading issue 22 (#22). But it didn't.

Here is the deal:

Instead of just a plain module, what if I put the module inside an anonymous closure. So something like this:

(function() {
    var http = require("http"),
    fs = require("fs");
    function helper(app) {
        return {
            get_watch_list: function () { console.log('1');},
            update_watch_list: function (r_ip) {console.log('2'); }
        };
    }
    module.exports = helper;
}());

rewire this module, and try out the get method gave me ReferenceError: *** is not defined.

var rewire = require('rewire');
var helper = rewire('./helper');
helper.__get__('http');
helper.__get__('fs');

Can I rewire the result returned by the anonymous function?

Cannot rewire within IIFE

My source looks like:

(function (sut) {
    'use strict';
    var testVar = 'Hello from original';

    sut.testMe = function(){
         return testvar;
    }
}(module.exports));

trying to test this gives me a failure:

(function () {
    'use strict';
    var rewire = require("rewire");
    var sut = rewire('../../lib/sut');
    sut.__set__('testVar', 'hello from mock');

    describe('Rewire', function(){
         it('fails', function(){
              expect(sut.testMe()).to.equal('Hello from mock');
         })
    }
)

The mock is not respected and the source has the original value for the testVar.
But if I remove my src from the IIFE then everything works. Am I doing something wrong?

Contexts for using rewrite

(This is more of a question than an issue)

First, thank you for writing and sharing rewire!

Now, here's my point of confusion. It seems like rewire might be super cool when dealing with code that you don't control, wherein you want to inject mocks but have hardwired dependencies.

But, if you are writing your own code, why hardwire, only to use rewire for testing. Why not just wire for testing and production separately, in each case with the appropriate objects? Is the idea that you've got one main use case (production), you it's easier to rewire (for testing) with this module?

Thank you!
Dmitry

Object referencing other object

I have a module like this:

var bar = 1;
var foo = {
    bar: bar
}

module.exports = {
    getBar: function () {
        return bar;
    },
    getFoo: function () {
        return foo;
    }
};

And a test like:

var assert = require("assert");
var rewire = require("rewire");
var subject = rewire('../../services/foo');

subject.__set__('bar', 2);

// this passes, bar is 2    
var bar = subject.getBar();
assert.equal(bar, 2);

// this doesn't foo.bar is the original value
var foo = subject.getFoo();
assert.equal(foo.bar, 2);

I would expect that foo.bar would be 2, but I am getting 1.

Would it be possible for rewire to do this?

Error: No wrapper for core module ...

I wanted to execute nof5 on current state of alamid and this error appeared:

Error: No wrapper for core module: "module" from directory "/home/topa/Workspaces/alamid/node_modules/rewire/lib" while processing file /home/topa/Workspaces/alamid/node_modules/rewire/lib/rewire.js
at moduleError (/usr/local/lib/node_modules/nof5/node_modules/browserify/lib/wrap.js:336:16)
at Function. (/usr/local/lib/node_modules/nof5/node_modules/browserify/lib/wrap.js:355:23)
at Function.require (/usr/local/lib/node_modules/nof5/node_modules/browserify/index.js:197:28)
at /usr/local/lib/node_modules/nof5/node_modules/browserify/lib/wrap.js:463:14
at Array.forEach (native)
at Function. (/usr/local/lib/node_modules/nof5/node_modules/browserify/lib/wrap.js:445:27)
at Function.require (/usr/local/lib/node_modules/nof5/node_modules/browserify/index.js:197:28)
at /usr/local/lib/node_modules/nof5/node_modules/browserify/lib/wrap.js:463:14
at Array.forEach (native)
at Function. (/usr/local/lib/node_modules/nof5/node_modules/browserify/lib/wrap.js:445:27)

Strange module path string behaviour (with webpack)

Hi,

not sure if this is related to rewire or rewire-webpack, so I will post it here anyway.

I'm having a strange behaviour when passing a path to the module that needs to be "rewired". Here the example:

// this works
let foo = rewire('../foo')

// this doesn't work
let name = 'foo'
let foo = rewire('../' + name)
// TypeError: Cannot read property 'call' of undefined
//   at rewire (webpack:///~/rewire-webpack/lib/rewire.web.js:13:0)
// https://github.com/jhnns/rewire-webpack/blob/master/lib/rewire.web.js#L10

If I log the argument passed to rewire I get this

# when it works
233

# when it doesn't
../foo 

Basically when it works I get some sort of index which is the key of the __webpack_modules__ object. When it fails, a string is passed hence the TypeError since it's not a valid key.

Any idea what's going on here? Any help is much appreciated, thanks :)

Uncaught Error: Cannot find module 'rewire'

I've included the webpack client side bundler as described, but the error from the title occurs at webpack-module:///x/x/x/x/x/lib//nof5/lib/bundler/webpackLoader.js!/home/topa/Applications/nvm/v0.8.7/lib//nof5/lib/bundler/webpackEntry.js

The file looks like this:

/* this line was injected by rewire() / var global = window; eval(require("rewire").getImportGlobalsSrc()); (function () { var rewire = require("rewire"); rewire.register(module, function set() { arguments.varName = arguments[0]; arguments.varValue = arguments[1]; arguments.src = ""; if (typeof arguments[0] === "object" && arguments.length === 1) { arguments.env = arguments.varName; if (!arguments.env || Array.isArray(arguments.env)) { throw new TypeError("set expects an object as env"); } for (arguments.varName in arguments.env) { if (arguments.env.hasOwnProperty(arguments.varName)) { arguments.varValue = arguments.env[arguments.varName]; arguments.src += arguments.varName + " = arguments.env." + arguments.varName + "; "; } } } else if (typeof arguments.varName === "string" && arguments.length === 2) { if (!arguments.varName) { throw new TypeError("set expects a non-empty string as a variable name"); } arguments.src = arguments.varName + " = arguments.varValue;"; } else { throw new TypeError("set expects an environment object or a non-empty string as a variable name"); } eval(arguments.src); }, function get() { arguments.varName = arguments[0]; if (arguments.varName && typeof arguments.varName === "string") { return eval(arguments.varName); } else { throw new TypeError("get expects a non-empty string"); } }); rewire = undefined;
require(/
/home/topa/Workspace/alamid/test/client/registries/pageRegistry.test.js /88);
require(/
/home/topa/Workspace/alamid/test/client/PageLoader.class.test.js /78);
require(/
/home/topa/Workspace/alamid/test/client/ViewCollection.class.test.js /80);
require(/
/home/topa/Workspace/alamid/test/client/View.class.test.js /79);
require(/
/home/topa/Workspace/alamid/test/client/Page.class.test.js /77);
require(/
/home/topa/Workspace/alamid/test/client/remoteRequest.test.js /89);
require(/
/home/topa/Workspace/alamid/test/client/attachPushHandlers.test.js /81);
require(/
/home/topa/Workspace/alamid/test/client/helpers/historyAdapter.test.js /83);
require(/
/home/topa/Workspace/alamid/test/client/helpers/domAdapter.test.js /82);
require(/
/home/topa/Workspace/alamid/test/client/helpers/jQuery.test.js /84);
require(/
/home/topa/Workspace/alamid/test/client/helpers/page.test.js /85);
require(/
/home/topa/Workspace/alamid/test/client/DisplayObject.class.test.js /76);
require(/
/home/topa/Workspace/alamid/test/client/App.class.test.js */75);
})();
// WEBPACK FOOTER //
// module.id = 0
// module.realId = 0
// module.chunks = main
//@ sourceURL=webpack-module:///home/topa/Applications/nvm/v0.8.7/lib//nof5/lib/bundler/webpackLoader.js!/home/topa/Applications/nvm/v0.8.7/lib//nof5/lib/bundler/webpackEntry.js

You can check out my implementation here: https://github.com/peerigon/alamid/blob/master/test/client/nof5.webpack.hooks.js

Any ideas?

With Coffeescript?

This module is the bees knees! Thanks for sharing it!

I'm curious, though, is anyone using it with CoffeeScript? It's an issue because CS wraps the compiled file's javascript in an anonymous, executed-in-place function. The members I'm snubbing with set are in the global namespace -- not the ones defined inside the function... which means they never get overridden.

My solution for the moment is to prefix the member I want to snub with @. If this was done in your example, the difference would be that the line would read: this.fs = require('fs');

I guess I can't think of a great reason this would be a terrible idea since node keeps modules separate already and I'm not polluting the global namespace much, really...

Any thoughts on this subject?

Cheers !

Add possibility to override require()

Given the following code:

var Agent = require('https').Agent;

module.exports.agent = new Agent({
    maxSockets: 5
  });

We cannot overwrite Agent. However, in the following code we can overwrite Agent with a spy:

var Agent = require('https').Agent;
module.exports.agent = function () {
  return new Agent({
    maxSockets: 5
  });
};

The difference is that the export is assigned to a function. It would be nice if we didn't have to assign the export to a function as the first block is cleaner.

Usage with mocha

I have small test case...
I want to replace some methon on global variable which is Sequelize model:

var models  = ... // sequelize models
var Product = models.Product;
    describe('read', function () {
      var modelsActions = rewire(projectPath + 'lib/excel/modelsActions.js');

      var products = [{name: "Some product from DB"}];
      var fakeFindAll = sinon.stub().returns(Promise.resolve(products));

      var revertRewire;

      before(function () {
        revertRewire = modelsActions.__set__('Product.findAll', fakeFindAll);
      });

      after(function () {
        revertRewire();
      });

      it('should load all products from DB', function () {
        modelsActions.read();
        fakeFindAll.should.be.called;
      });

      it('should order by ascending ID', function () {
        modelsActions.read();
        fakeFindAll.should.be.calledWith(sinon.match({order: 'id ASC'}));
      });

      it('should load without limit', function () {
        modelsActions.read();
        fakeFindAll.should.be.calledWith(sinon.match({limit: 0}));
      });
    });

But it throws:

    main functional
      read
        ✓ should load all products from DB
        ✓ should order by ascending ID
        ✓ should load without limit
        1) "after all" hook

  4 passing (314ms)
  9 pending
  1 failing

  1) Model Actions tests main functional read "after all" hook:
     TypeError: Cannot read property 'findAll' of undefined
      at Object.eval (eval at __set__ (/srv/lib/excel/modelsActions.js:273:19), <anonymous>:1:40)
      at Object.__set__ (/srv/lib/excel/modelsActions.js:273:5)
      at /srv/lib/excel/modelsActions.js:276:24
      at Context.<anonymous> (/srv/test/lib/excel/modelsActions.js:149:9)
      at callFn (/usr/local/lib/node_modules/mocha/lib/runnable.js:251:21)
      at Hook.Runnable.run (/usr/local/lib/node_modules/mocha/lib/runnable.js:244:7)
      at next (/usr/local/lib/node_modules/mocha/lib/runner.js:259:10)
      at Immediate._onImmediate (/usr/local/lib/node_modules/mocha/lib/runner.js:276:5)
      at processImmediate [as _immediateCallback] (timers.js:349:17)

If I get variable and then set it back, everything works like expected.

      var oldProductFindAll;

      before(function () {
        var oldProductFindAll = modelsActions.__get__('Product.findAll');
        modelsActions.__set__('Product.findAll', fakeFindAll);
      });

      after(function () {
        modelsActions.__set__('Product.findAll', oldProductFindAll);
      });

It's a bug or I'm using it wrong?

Can't rewire strict file

foo.js

'use strict';

function foo() {
  console.log(document);
}

module.exports = foo;
> var rewire=require('rewire');
undefined
> var foo = rewire('./foo');
undefined
> foo.__set__('document', 5);
ReferenceError: document is not defined
    at Function.eval (eval at __set__ (/Users/chris/Coding/foo.js:71:19), <anonymous>:1:10)
    at Function.__set__ (/Users/chris/Coding/foo.js:71:5)
    at repl:1:5
    at REPLServer.self.eval (repl.js:110:21)
    at repl.js:249:20
    at REPLServer.self.eval (repl.js:122:7)
    at Interface.<anonymous> (repl.js:239:12)
    at Interface.emit (events.js:95:17)
    at Interface._onLine (readline.js:203:10)
    at Interface._line (readline.js:532:8)
> 

Works if "use strict" is removed. Babel automatically adds "use strict" to the tops of files when they're loaded, so rewire doesn't work with ES6/Babel modules.

Rewiring within an anonymous function

I have a module that looks like

"use strict";

var inherits = require('util').inherits;
var Task = require("task");
var db = require('db');

// Constructor
function FooTask() {
    Task.apply(this);
}
inherits(FooTask, Task);

// Prototype method
FooTask.prototype._run = function() {
    var self = this;

    // doSomething.call(self, null); // Method is mocked here!

    db.getStuff()
    .then( function(result) {
        doSomething.call(self, result); // Method is not mocked here!
        // Do other stuff
    })
    .fail( function(err) {
        console.error('Rut roh');
    });
};

// Private method
var doSomething = function(result) {
    // Does a bunch of stuff
};

// Return the module exports
module.exports = FooTask;

My spec file looks like

var rewire = require('rewire');

var BaseTask = rewire("task");
var Task = rewire("fooTask");

// Some other tests

it("should test something", function(done) {
    Task.__with__({
        doSomething: function() {
            console.log('doSomething');
        },
        db: {
            getStuff: function() {
                // Return resolved promise
            }
        }
    })(function() {
        var testTask = new Task();
        var returnVal = testTask._run();
        expect(returnVal).toBe(0);
    });
});

When I run the test, the doSomething method is not mocked. Instead, the _run method calls the proper doSomething. If I were to run doSomething above the db.getStuff call, then the mocked version is called.

Is this expected? Is there anyway to mock doSomething within that callback?

Can't rewire an Express app

Hi!

I'm trying to rewire the routing of an Express.js app to tests it with mocha and superagent, but it's not working.
The mocha test begins with:

var rewire = require('rewire');
var app = rewire('../app.js');
// Rewire the backend routing
app.__set__('backend', function() {//Dummy});
var server = http.createServer(app);
describe('Homepage', whatEverTests);

Snippet from app.js:

var express = require('express')
  , backend = require('./routes/backend.js');

var app = express();
module.exports = app;

Mocha test runs as if I have not called rewire on app.js and set backend to a dummy function at all.

Yelp!
Thanks.

Working with relative paths

Writing the following seems impossible:

var log = rewire('./log');
log.__set__("../config", {});

Mocha complains with a SyntaxError: Unexpected token ..

Is there something wrong in this?
Thanks for your help.

rewire is not working in the browser

As the title already said rewire seems not to work with the browser.

I'm using the middleware for use with browserify in nof5 like described in readme.
But it seems that the following conditional does not work:

if (process.title === "browser") {
    module.exports = require("./browserify/browserifyRewire.js");
} else {
    ...
}

process.title is still "node" and the require("./browserify/browserifyRewire.js") is not happening.
You can take a look for my implementation here.

This error is thrown:

Error: No wrapper for core module: "module" from directory "my/dir" while processing file /my/dir/project_anme/node_modules/rewire/lib/rewire.js

If rewrite the code and force to use require("./browserify/browserifyRewire.js"); it breaks because of this reason:

ReferenceError: window is not defined

Not sure what is wrong ...

Rewire and Classes + Constructors + Prototypes

Hello everyone,

First I would like to thanks in advance for any help or guidance that could be provided. I am new with rewire and I wanted to mock up a class with a constructor and several prototypes. A really trivial example of what I am trying to do, is to mock User Class in the code bellow, I would appreciate any guidance.


User Class
function User(name) {
    this.name = name;
}

User.prototype.getName = function getName() {
    return this.name;
};

module.exports = User;

Company Module
var user = require('user');

var users = [];

exports.setUser = function setUser(firstName, lastName) {
    users.push(new User(firstName + lastName));
}

exports.getUser = function getUser(id) {
    return users[id].getName();
}

Test Module
var company = rewire('./company');

// Here is were I would need to mock the User class.
var userMock = {
    .... // How to set the User constructor?
    .... // Prototypes follow the same pattern as exports?
    getName: function getName() {
         // Testing code behavior with empty results for example
         return null;
    }
}

company.__set__('user', userMock);

Thanks in advance for your time and support

Object.defineProperty called on non-object with mocha

Description

I believe that may be related to #51 given it produces the same error.

When using mocha and rewire with a module that exports a named function directly, an error occurs:

TypeError: Object.defineProperty called on non-object

Running without mocha and using node directly works correctly.

Changing from a named function to assigning the function (see test files below) and exporting the variable instead works.

Example

var foo = function () {};
module.exports = foo;

Test Files

index.js

var rewire = require('rewire');
var test = rewire('./test');

test.js

function run() {}
module.exports = run;

Gist

https://gist.github.com/MitMaro/eb09a24817bcc58f9421

Running

npm install
mocha index.js

Stacktrace

/Users/mitmaro/Code/rewire.mocha.bug/test.js:4
Object.defineProperty(module.exports, '__get__', {enumerable: false, value: fu
       ^
TypeError: Object.defineProperty called on non-object   
    at Function.defineProperty (native)
    at Object.<anonymous> (/Users/mitmaro/Code/rewire.mocha.bug/test.js:4:8)
    at Module._compile (module.js:446:26)
    at Object.Module._extensions..js (module.js:464:10)
    at Module.load (module.js:341:32)
    at Object.load (/Users/mitmaro/Code/rewire.mocha.bug/node_modules/rewire/lib/moduleEnv.js:19:18)
    at internalRewire (/Users/mitmaro/Code/rewire.mocha.bug/node_modules/rewire/lib/rewire.js:50:15)
    at rewire (/Users/mitmaro/Code/rewire.mocha.bug/node_modules/rewire/lib/index.js:11:12)
    at Object.<anonymous> (/Users/mitmaro/Code/rewire.mocha.bug/index.js:2:12)
    at Module._compile (module.js:446:26)
    at Object.Module._extensions..js (module.js:464:10)
    at Module.load (module.js:341:32)
    at Function.Module._load (module.js:296:12)
    at Module.require (module.js:351:17)
    at require (module.js:370:17)
    at /Users/mitmaro/Code/rewire.mocha.bug/node_modules/mocha/lib/mocha.js:192:27
    at Array.forEach (native)
    at Mocha.loadFiles (/Users/mitmaro/Code/rewire.mocha.bug/node_modules/mocha/lib/mocha.js:189:14)
    at Mocha.run (/Users/mitmaro/Code/rewire.mocha.bug/node_modules/mocha/lib/mocha.js:422:31)
    at Object.<anonymous> (/Users/mitmaro/Code/rewire.mocha.bug/node_modules/mocha/bin/_mocha:398:16)
    at Module._compile (module.js:446:26)
    at Object.Module._extensions..js (module.js:464:10)
    at Module.load (module.js:341:32)
    at Function.Module._load (module.js:296:12)
    at Function.Module.runMain (module.js:487:10)
    at startup (node.js:111:16)
    at node.js:799:3

webpack 0.9

For webpack 0.9 support I would design rewire as webpack plugin.

The interfaces are not yet documented but here is what you will need:

A plugin must have a apply function with on argument (the compiler).

The Compiler extends Tapable so it exposes a plugin method.


You want to handle "normal" modules which are created by the normal-module-factory. It can be intercepted at after-resolve so you could enforce a loader by adding it to loaders.

The ModuleAliasPlugin from enhanced-resolve can rename a module i.e. rewire to rewire/bundlers/webpack. Alternatively you could change it in after-resolve.

function RewirePlugin() {
}
RewirePlugin.prototype.apply = function(compiler) {
  compiler.plugin("normal-module-factory", function(nmf) {
    nmf.plugin("after-resolve", function(data) {
      data.loaders.unshift(path.join(__dirname, "postloader.js"));
    });
  });
  var aliasPlugin = new ModuleAliasPlugin({ rewire: __dirname });
  compiler.resolvers.normal.apply(aliasPlugin);
  compiler.resolvers.context.apply(aliasPlugin);
};

You can get the "native" require function inside a compiled module with __webpack_require__.


Better but more complex solution:

You may want to make rewire("../lib/myModule.js"); behave similar to require("../lib/myModule.js");, but with a extra loader.

Take a look at these files:

You need to write a own RewireDependency which is very simple.

You need to register the plugin call rewire on compiler.parser and create the RewireDependency.

In the RewirePlugin you need to bind RewireDependency to a template (ModuleDependencyTemplateAsId is suitable) and to a factory.

Write a simple factory (decorator), which decorates the NormalModuleFactory.

function RewireFactory(factory) {
  this.factory = factory;
}
RewireFactory.prototype.create = function(context, dependency, callback) {
  this.factory.create(context, dependency, function(err, module) {
    if(err) return callback(err);
    module.loaders.unshift(...);
    module.request = "rewired " + module.request;
    // modules with equal module.request are threaded as equal,
    // so we have to choose a unique one.
    return callback(null, module);
  });
}

The result: rewire(/* ... */ 123), with 123 is the module id.

To inject the rewire function where used take a look at https://github.com/webpack/webpack/blob/master/lib/ConsolePlugin.js

Add it when adding the RewireDependency.

You can access the modules with __webpack_modules__, it's a object with key is module id and value is a function(module, exports, require).


Tobias

Cannot convert null to object

I've decided to give rewire a try, but I'm getting an error.

I've simplified my code to just this

var React = require("react/addons");
var rewire = require("rewire");

But I keep getting an error saying "Cannot convert null to object at /Users/mdell/Work/site/node_modules/rewire/lib/index.js:16:22"

Any ideas?

Rewire fails when using fs.readFileSync

When I use sinon in combination with rewire and try to stub fs.readFileSync.
I get following error:

❯ npm test

[email protected] test /Users/kwakayama/CODE/rewireTest mocha
readFile 1) "before each" hook

0 passing (9ms) 1 failing

1) readFile "before each" hook: TypeError: Cannot call method 'charCodeAt' of undefined at stripBOM (module.js:464:15) at Object.Module._extensions..js (module.js:474:19) at Module.load (module.js:356:32) at Object.load (/Users/kwakayama/CODE/rewireTest/node_modules/rewire/lib/moduleEnv.js:19:18) at internalRewire (/Users/kwakayama/CODE/rewireTest/node_modules/rewire/lib/rewire.js:50:15) at rewire (/Users/kwakayama/CODE/rewireTest/node_modules/rewire/lib/index.js:11:12) at Context. (/Users/kwakayama/CODE/rewireTest/test.js:15:20) at callFn (/Users/kwakayama/CODE/rewireTest/node_modules/mocha/lib/runnable.js:251:21) at Hook.Runnable.run (/Users/kwakayama/CODE/rewireTest/node_modules/mocha/lib/runnable.js:244:7) at next (/Users/kwakayama/CODE/rewireTest/node_modules/mocha/lib/runner.js:259:10) at Object._onImmediate (/Users/kwakayama/CODE/rewireTest/node_modules/mocha/lib/runner.js:276:5) at processImmediate as _immediateCallback

npm ERR! Test failed. See above for more details. npm ERR! not ok code 0

I created a sample repo to recreate the issue:
https://github.com/kwakayama/rewireTest
Can someone give me a hint how to fix this issue?

best,
kentaro

Rewiring a nested module

I would like to rewire a module that sits quite far down the require hierarchy. Simply rewiring it at the top level is not enough. I then tried something like this:

# This in the top level on its own doesn't work
validate = rewire './validate'
validate.__set__ 'func', -> true

moduleD = rewire './moduleD'
moduleD.__set__ 'validate', validate

moduleC = rewire './moduleC'
moduleC.__set__ 'moduleD', moduleD

moduleB = rewire './moduleB'
moduleB.__set__ 'moduleC', moduleC

moduleA = rewire './moduleA'
moduleA.__set__ 'moduleB', moduleB

.. but the original validate function is still always called. A console.log at the top of validate shows it's being loaded twice - shouldn't it only be loaded once? Validate is required at least 5 different times in the app. I even rewired all of those modules with the rewired validate, but nothing has worked.

Is this a bug, not a feature, or user error?

npm test returns an error

I am at master and when I run npm test, I get this error.

 14 passing (86ms)
  1 failing

  1) rewire "before all" hook:
     Error: ENOTEMPTY, directory not empty '/Users/pguruprasad/Projects/rewire/test/testModules/fake_node_modules'
      at Object.fs.renameSync (fs.js:439:18)
      at Context.<anonymous> (/Users/pguruprasad/Projects/rewire/test/rewire.test.js:16:16)
      at Hook.Runnable.run (/Users/pguruprasad/Projects/rewire/node_modules/mocha/lib/runnable.js:211:32)
      at next (/Users/pguruprasad/Projects/rewire/node_modules/mocha/lib/runner.js:249:10)
      at Runner.hook (/Users/pguruprasad/Projects/rewire/node_modules/mocha/lib/runner.js:261:5)
      at process.startup.processNextTick.process._tickCallback (node.js:245:9)



npm ERR! weird error 1
npm ERR! not ok code 0

The error says something about test/testModules/fake_node_modules not being empty. Is this because I am on an unsupported node version ?

➜  rewire git:(master) node --version
v0.8.24

Injecting another rewired module as a dependency

So let's say I am testing A, and A has B as a dependency. There is a dependency that B has that I want to modify (say it's fs because I dont want file writes when im testing), can I rewire B and then rewire A so it uses the rewired version of B instead of the actual dependency? So far i'm struggling with this eg:

//StubbedScraper.js
var Scraper = rewire(__dirname + '/../../../lib/Scraper'),
winstonStub = require(__dirname + '/../stubs/winston.stub'),
listingStub = require(__dirname + '/../stubs/Listing.stub'),
fsStub = require(__dirname + '/../stubs/fs.stub');

Scraper.set('Listing', listingStub);
Scraper.set('winston', winstonStub);
Scraper.set('fs', fsStub);

module.exports = Scraper;


var specialScraper = rewire('../specialScraper') <<< this has Scraper from above as a dependency
stubbedScraper = require('../stubbedScraper)

specialScraper.__set('Scraper', stubbedScraper);


So far it looks like even though it should be using the stubbed instance it's reverting to the original version of fs instead of my fs stub...is this behaviour expected, any solutions to this except writing a completely fresh Scraper stub.

rewire doesn't work with babel

I've just finished porting all of my React.js code to ES6 and found that my tests have broken due to Rewire (seemingly) not supporting the new syntax.

// DummyClass.js
import states from 'utils/states';

export default class DummyClass {
  constructor() {}
}

// DummyClass-spec.js
describe('Test', function() {
    it('should work', function() {
       var rewire = require('rewire');
       var DummyClass = rewire('./DummyClass');
       DummyClass.__set__('states', 'foo'); // throws ReferenceError: states is not defined
    })
});

However, when importing using var states = require('utils/states') everything is just fine.

Any assistance would be appreciated!

Rewiring 'require'

Hi,

I came across a situation where I need to to test the parameters of a require().
In my case I dynamically generate the path of a module to require (because I support versioning) and want to make sure it does it as expected.

Does rewiring require makes sense in such situation? Is there a way to accomplish that?

Thanks!

Calling rewire() requires a module twice

Hi
As opposed to require which actually "requires once" a given module, rewire seems to be running each time there is a rewire() call, even for the same module.

This presents an issue when for example I'm using rewire to require a mongoose model because I'm getting an exception saying "Cannot overwrite someModel model once compiled".

Am I doing something wrong?

Incorrect functions called when functions have same names across different files.

Hello,
First of all, thank you very much for writing and supporting rewire.

We are using rewire along with grunt, mocha and chai for unit-testing. Currently, we are triggering the tests of 3 of our main files through a single grunt command. These 3 index.js files (located in different folders) have few functions with the same name for example a function getDocument is present in /folder1/index.js as well as /folder2/index.js. For each of the index.js files, we have a corresponding test-index.js file and a variable "Router" that is rewired to the particular location of index.js file as follows:

Router = rewire('/routes/folder1/index.js');
Router = rewire('/routes/folder2/index.js');
Router = rewire('/routes/folder3/index.js');

In each of the test files, we access a particular function by following commands:
snippet

The problem occurs when a function like (getDocument) appears in more than one index.js files. In that situation, a testcase corresponding to /folder2/index.js that has its Router pointing to /folder2/index.js still calls the getDocument of /folder1/index.js. One of the workarounds was to change the name of the function so that no two functions between any of the index.js files have same names. However, this is not desirable.
I looked into other defects (open and closed ones) and seems like this problem is the one similar to caching where the file that has been rewired earlier (/folder1/index.js) has its functions in the cache and /folder2/index.js in unable to over-ride the function with same name. I also tried the following solution as was mentioned in one of the issues but it did not work for me:

var myModel = rewire('./lib/myModel');
require.cache[require.resolve('./lib/myModel')] = {
exports: myModel
};

Any help/pointers on this issue would be much appreciated.
Thanks in advance!

strict mode not detected if under a comment

Strict mode is successfully detected when it is at the very top of the file but if you have a comment before it strict mode will not be detected.
Works:

'use strict';
// This program ...

Does not work:

// This program ...
'use strict';

This can be remedied by adding an 'm' modifier to the end of the regular expression in detectStrictMode.js:

/^\s*(?:"use strict"|'use strict')[ \t]*(?:[\r\n]|;)/gm

can rewire worked with module exported functions?

I'm just starting out using rewire. One of my script files that I need to test is :

helper = 
  get_watch_list: ->

  update_watch_list: (r_ip)->

module.exports = helper

Now when I do in my test

rewire "./helper"
console.log helper
#{ get_watch_list: [Function],
#  update_watch_list: [Function],
#  __set__: [Function: __set__],
#  __get__: [Function: __get__] }

I see that get and set methods are added to the helper object. But if I change my script to use an exported function instead of an object,

helper = (app) ->
  get_watch_list: ->

  update_watch_list: (r_ip)->

module.exports = helper

and when I try to rewire it in my test, I don't see the get and set methods.

rewire ("./helper")(app)
console.log helper
# {  get_watch_list: [Function],
#  update_watch_list: [Function] }

Changing require result instead of variables

First of all: I love the way rewire is built and I really see the advantages by keeping and using an intact require function.

The only drawback i can see by doing this instead of the node-sandboxed-module approach, is that you have to keep track of variable names being used in the module being mocked. With the altering-require-function approach, one only needs to remember which module the mocked module is dependent upon.

For example, let's say we have the following module foo.js:

var helper = require('./utils.js');

If I would like to rewire the module for better testing I would do something like this:

var module = rewire('./foo.js');
module.__set__({
  helper: function() { ... } //Need to keep track & remember variable name 'helper' being used in module.
});

With node-sandboxed-module I would do something like this:

var sm = require('sandboxed-module');
var module = sm('./foo.js', {
  requires: {
    'utils.js': function() { ... } //Need to keep track & remember module dependency name.
  }
});

The second approach has two advantages in my opinion:

  • Variables are more likely to be renamed than module names.
  • If the module utils.js is required at many places in a module, assigned to different variable names, one can mock all of those variables by simply mocking the required module name.

Now, I understand that this might not be the natural way of doing this since rewire doesn't alter the require function, but I wonder if there is any way of mimicking this feature? Maybe rewire could check which variable names are assigned to the required module that the user would like to mock and then simply altering the variables for the user.

Something like this:

//In module foo.js
var helper1 = require('./utils.js');
var helper2 = require('./utils.js');
var helper3 = require('./utils.js');

//In another module wishing to mock foo.js module.
var module = rewire('./foo.js');

//Mock all variables assigned to the required module 'utils.js'
module.__set__({
  requires: {
    'utils.js': function() { ... }
  }
});

//The above call would be translated to the following:
module.__set__({
  helper1: function() { ... },
  helper2: function() { ... },
  helper3: function() { ... }
});

What do you think? :)

Code coverage tool does not recognize rewired modules

Hi,
I am using Istanbul for coverage reports and I realized it does not consider the rewired modules "covered". For example if myModule.coffee is the source and myModule.spec.coffee is the test containing the following:

myModule = rewire 'src/myModule'

Is there a way to fix this?
Regards

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.