rewire does not work with babel
panuhorsmalahti opened this issue ยท 26 comments
ES6 modules (compiled with babel) don't work with rewire.
Same as jhnns/rewire-webpack#12
This is not an ES6 issue, but caused by the way how babel emulates the new module system.
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?
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?
It looks like somebody did: https://github.com/speedskater/babel-plugin-rewire
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 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 - 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 import
ed 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