rewire icon indicating copy to clipboard operation
rewire copied to clipboard

rewire does not work with babel

Open panuhorsmalahti opened this issue 9 years ago • 26 comments

ES6 modules (compiled with babel) don't work with rewire.

panuhorsmalahti avatar Jun 11 '15 11:06 panuhorsmalahti

Same as jhnns/rewire-webpack#12

This is not an ES6 issue, but caused by the way how babel emulates the new module system.

jhnns avatar Jun 24 '15 09:06 jhnns

Further insights: babel/babel#1337

Unfortunately, it's not easy to solve, because it requires a major rewrite. And tbh: I'm not very motivated, because babel's change, which is causing the problem, was driven by the misconception eval() was deprecated in ES6.

jhnns avatar Jun 24 '15 09:06 jhnns

:+1: for being honest and saying it's not motivating. You ok with the idea that someone else does it and sends you a PR about it?

mikaturunen avatar Jun 24 '15 10:06 mikaturunen

Definitely :+1:

The tests, however, should stay the same (and they should still run afterwards). Since it would be a great rewrite, I'd appreciate if you could do this in close consultation. @speedskater has already contacted me.

jhnns avatar Jul 12 '15 20:07 jhnns

Is anybody working on this?

jabhishek avatar Sep 30 '15 11:09 jabhishek

It looks like somebody did: https://github.com/speedskater/babel-plugin-rewire

GreenGremlin avatar Oct 06 '15 17:10 GreenGremlin

Just landed here after headbutting my code for a while and then looking for information on this. Wondering if it would be a good idea to have a paragraph about this in the readme to avoid people trying to do this with rewire in the first place. I can submit a PR :)

trodrigues avatar Nov 09 '15 09:11 trodrigues

@trodrigues you can do that. However, it should not sound like it's impossible to use rewire with ES2015 modules. I just have to figure out a good way how to hook-in rewire. I'll probably also have to refactor the public API so that rewire "ports" like rewireify are easier to integrate (also see #78)

jhnns avatar Nov 11 '15 15:11 jhnns

Right. But I think the main issue here is not ES6 modules in themselves but transpiled ES6 modules. I think that's always going to be a case specific to babel users.

trodrigues avatar Nov 20 '15 14:11 trodrigues

I think I might be having a similar issue.

     TypeError: Filename must be a string
      at internalRewire (node_modules/rewire/lib/rewire.js:19:15)
      at rewire (node_modules/rewire/lib/index.js:11:12)

This seems to happen only when I webpack my test files and their es6 deps and run them in mocha / node. I don't see any issues when I run the same tests thru my karma config and run them in the browser. The funny thing is that the webpack config should be about the same for both karma and mocha. I'll check for differences, but any suggestions would be greatly appreciated.

Thanks!

adjavaherian avatar Jan 21 '16 22:01 adjavaherian

Just FYI, my approach to 'fix' this is:

import _config from '../config';
let config = _config;

Then rewire as normal:

const thing = rewire('./path/to/thing');
thing.__set__('config', testConfig);

It's a little dirty as I don't like changing production code for test purposes. I normally add a comment like // This is to allow rewire its hooks

jamlen avatar Sep 05 '16 13:09 jamlen

@jamlen Thank you - that really solves my problem for now!

mdunisch avatar Mar 10 '17 12:03 mdunisch

@jamlen Good solution!!

My assumption is that import modules in ES6 are frozen - thus Rewire can't dynamically modify what's being imported - hence why your solution, @jamlen, works?

gregorydrake avatar May 09 '17 16:05 gregorydrake

@jamlen - pardon my ignorance, but is the rewire('./path/to/thing') the workaround for rewire('../config')?

pilphil avatar Aug 15 '17 06:08 pilphil

@pilphil the rewire('./path/to/thing') is just the path to the class under test.

So if you have:

.
├── lib
│   └── thing.js
└── test
    └── test-thing.js

and you are writing the test file ./test/test-thing.js, then this would be rewire('../lib/thing')

jamlen avatar Aug 15 '17 18:08 jamlen

Is this the right place/issue to ask about support for mocking out calls to imported dependencies like fs etc.?

robertmain avatar Oct 17 '17 02:10 robertmain

@robrtmain you could check out this answer of mine if stackoverflow... https://stackoverflow.com/a/30730818/2140627

jamlen avatar Oct 17 '17 15:10 jamlen

@jamlen that's fine but it looks like you're using require(), rather than import which is what I was asking :)

I know it works with require - what I was trying to figure out was if I could use it with import in TypeScript

robertmain avatar Oct 17 '17 17:10 robertmain

@robertmain I have some examples somewhere... let me dig them out :) it does work (although I wasn't using TypeScript)

jamlen avatar Oct 18 '17 15:10 jamlen

My class looks like this:

import { EventEmitter } from 'events';

import _logger from './logger';
import { stock as mappers } from './mappers';
import { stock as _StockStore} from './stores';

let StockStore = _StockStore;
let logger = _logger;

let store, _debug;

/** StockMigrator */
module.exports = class StockMigrator extends EventEmitter {

    /** Create a StockMigrator. */
    constructor () {
        super();
        store = new StockStore();
        store.on('write_data', (type, key, duration) => {
            this.emit('progress', 'write', key, duration);
        });
    }
  // other methods here...
}

My tests look like this:

import rewire from 'rewire';
import deride from 'deride';

const Migrator = rewire('../../../src/stockMigrator');
import { stock as mappers } from '../../../src//mappers';
import data from '../../data/stock';
import config from '../../config';

describe('Unit tests', () => {
    describe('Stock Migration', () => {
        let migrator, store;

        describe('stockMigrator', () => {
            before(() => {
                store = deride.stub([ 'writeDocuments' ]);
                store.setup.writeDocuments.toDoThis(() => {
                    store.emit('status', 'write_data', 'doc123');
                    return Promise.resolve();
                });
                Migrator.__set__({
                    StockStore: () => store,
                    logger: deride.stub(['debug', 'info', 'warn', 'error', 'audit'])
                });
                migrator = new Migrator();
            });
  // describes here...

jamlen avatar Oct 18 '17 15:10 jamlen

@jamlen so you got rewire to work with import then?

robertmain avatar Oct 18 '17 22:10 robertmain

@robertmain Yes, but with the caveat that you need to add a let to allow rewire to be able to substitute the mock.

jamlen avatar Oct 19 '17 09:10 jamlen

ah!

robertmain avatar Oct 19 '17 13:10 robertmain

@jamlen The problem I'm running up against at the moment with this in TypeScript is that it seems I have to load the thing I'm testing with rewire....which doesn't return the correct kind of object (it has no constructor) and then the ts language server gets upset.

robertmain avatar Oct 20 '17 14:10 robertmain

@robertmain at a guess I'd say this is a TS specific issue and probably raise a new issue for it.

jamlen avatar Oct 23 '17 10:10 jamlen

Using rewire 4.0.1, my attempt to call import { rewire } from 'rewire'; is met with the error:

HeadlessChrome 74.0.3729 (Mac OS X 10.14.4) ERROR
  Uncaught SyntaxError: The requested module '../node_modules/rewire/lib/index.js' does not provide an export named 'rewire'
  at http://localhost:9876/context.html:0:0

johnthad avatar May 14 '19 15:05 johnthad