thlorenz / proxyquire

🔮 Proxies nodejs require in order to allow overriding dependencies during testing.
MIT License
2.75k stars 99 forks source link

`createRequire` support & a way to access the mocked `require` #265

Open PaperStrike opened 2 years ago

PaperStrike commented 2 years ago

The package I want to mock uses createRequire to require, which proxyquire doesn't proxy yet. To workaround, I tried:

proxyquire('./example', {
  module: {
    createRequire: (filename) => Object.assign((id) => require(id), {
      // ...some other stuff like `resolve`
    }),
    '@global': true,
  },
  // ... stubs I use
});

But then I found the require above can't be the mocked one. I wonder if you can support createRequire and give us a way to access the mocked require (so that we can mock createRequire to return a require with our own properties).

Thanks!

bendrucker commented 2 years ago

If it involves changing the API, probably not. But if you can find a way to make resolving work without API changes, PR welcome. Stubs would still be relative to the module under test or perhaps you'd resolve absolute paths.

ljharb commented 2 years ago

it'd be gross, but eval('require') would work :-p

bendrucker commented 2 years ago

Ok I think I get it, I think you want your replacement for module to respect the stubs passed to proxyquire in the same call. It's reasonable for proxyquire to expose a mocked require for that but getting it in scope is tricky. Without changing the API for existing users, maybe the stubs arg could also be a function that returns a stubs object. That function could receive a value that would expose the mocked require.

ljharb commented 2 years ago

That seems easy, nonbreaking, and in line with a number of userland APIs - as long as nobody was using the stubs object for a module that exports a function.

bendrucker commented 2 years ago

The stubs object I'm referring to is the map of module paths to module override values. So you can still override a module that exports a function.

So:

proxyquire('./foo', () => {module: {}})

That function would in reality receive an argument that exposes a require function which you could then use to in your mock module.

ljharb commented 2 years ago

Sounds great - maybe it could be sent an object, that for now only contains require, so it can be extended later?