jhnns/rewire

Doesn't work with ES6 consts

erbridge opened this issue ยท 28 comments

You get errors like this:

TypeError: Assignment to constant variable.
  at eval (eval at __set__ (my-module.js:73:19), <anonymous>:1:21)
  at Object.__set__ (my-module.js:73:5)
  at Context.<anonymous> (my-module-test.js:131:55)

+1 for this. My workaround was using let instead of const to enable it to rewire.

Yes, that works, and is my current workaround, but I really don't want to have to open the code up to bugs in order to test it...

jhnns commented

Well ... re-assigning const is not possible, rewire can't change this.

In order to do this, rewire would need to change the code (like @brenolf did manually). Therefore, we would need to use a parser which is currently out of scope.

However, if we want to support ES6 modules someday, we will need a parser anyway, so I'll consider this when rewriting rewire.

+1 for ES6 consts. Thanks to @brenolf for the workaround.

@erbridge @brenolf dcrockwell @dcrockwell For what it's worth, proxyquire seems to work fine using ES6 consts.

I've yet to write some more complex tests with it but initial results are encouraging.

I can't reproduce this issue.
I can rewire const functions.

jhnns commented

Do you have an example? I'm pretty sure that this can not work.

var r = rewire('./reducer');
const reduceAddAction = r.__get__('reduceAddAction');

Where reduceAddAction is a const function.

But maybe you were talking about overwriting?

jhnns commented

But maybe you were talking about overwriting?

Exactly

Riffing on what @kaktus42 was saying though, you can take advantage of the way JS const works for evil and profit in the case when it's an object:

var r = rewire('./reducer');
const reduceAddAction = r.__get__('reduceAddAction');
reduceAddAction.method.to.stub = sinon.stub();

In this case you're not reassigning the variable identifier, and you're changing a referenced value, so it is possible, whether or not it's a good idea or not is another thing entirely ๐Ÿ˜„

jhnns commented

I think it's totally valid to change const objects, because const does not mean that the object itself is frozen (you could use Object.freeze() for that ๐Ÿ˜‰ ). However, I would not change the code just for the test framework.

+1 for allowing rewire of const's

+1 again!

you should check this out : https://mathiasbynens.be/notes/es6-const
+1 for rewire

+1 yet again!

+1 for const and for full es6 support...

@rewiregps You link an article that very well articulates why this cannot work. The bindings are immutable. So this already works exactly how it should. You cannot override a const binding, which is what rewire does.

jhnns commented

@shivarajnaidu I'm thinking about ES6 support, but it's not that easy :) โ€“ especially as long as the loader spec is not finished yet.

Well, in my case, I needed to stub a method from a private const of a module. proxyquire supports this use case

For example

my-module.js

const http = require('http')

my-module-spec.js

const proxyquire = require('proxyquire');
const myModule = proxyquire('./my-module', { 'http': httpStub });

httpStub.Server = app => {
      return { key: 'value' };
    };
jhnns commented

Rewiring const is supported since 3.0.0

@jhnns 4.0.0 here, this does not work.

main.js
const { apiRoot, defaultPort } = require('config');

main.test.js

const myModule = rewire('myModule');

myModule.__set__('defaultPort', 123);

TypeError: Assignment to constant variable.

jhnns commented

@goyney Your example should work. Could you provide a demo that makes it reproducible. I did found a bug though where const was not properly detected when it was written like: const{ apiRoot .... I published a fix with 4.0.1

@jhnns I do have the same problem as @goyney :(
I already have rewire 4.0.1

The only difference I have is that it's not from destructuring.

Demo:
index.js:

const myPromisifiedFunction = promisify(someFunct.ToPromisify)

index.test.js:

const myModule = rewire('../myModule')
const myStub = sinon.stub()
const myStubbedFunction = myModule.__set__('myPromisifiedFunction', myStub)

And what I get when running tests is:
TypeError: Assignment to constant variable. at eval (eval at __set__ (index.js:159:5), <anonymous>:1:19) at Object.__set__ (index.js:159:5) at Object.<anonymous> (test/index.test.js:14:48)

As a possible "dirty" workaround, it's possible to __get__ the function you need to test (which you expect to call other funcs), convert it to string, eval and test it in the context of your test. It works for small functions, but I'm sure with larger ones it might be a bit painful.

// myfunc.js

const unmockable = (a) => `Hello ${a}`;
const myLovelyFunc = (a) => unmockable(a);

module.exports = { myLovelyFunc };

// - - - - - - - - - - - - - - - somewhere in myfunc.test.js - - - - - - - - - - - - - - -

const myLovelyFunc = rewire('./myfunc.js').__get__('myLovelyFunc');
const subject = eval(myLovelyFunc.toString());

// Redefining function's local context
const unmockable - jest.fn(`Bye, ${a}`);

subject('Dude'); //=> Bye, Dude

expect(unmockable).toHaveBeenCalledWith('Dude');

Still having this issue even with rewire 5.0.0, any fixes or workarounds?

Still having this issue even with rewire 6.0.0, any fixes or workarounds?

I just had to change es6 to es5 and it worked.