jhnns/rewire

rewire does not work with babel

panuhorsmalahti opened this issue ยท 26 comments

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

jhnns commented

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 commented

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.

๐Ÿ‘ 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?

jhnns commented

Definitely ๐Ÿ‘

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.

Is anybody working on this?

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 :)

jhnns commented

@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)

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.

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!

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 Thank you - that really solves my problem for now!

@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?

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

@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')

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

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

@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 I have some examples somewhere... let me dig them out :) it does work (although I wasn't using TypeScript)

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 so you got rewire to work with import then?

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

ah!

@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 at a guess I'd say this is a TS specific issue and probably raise a new issue for it.

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