request / request-promise

The simplified HTTP request client 'request' with Promise support. Powered by Bluebird.
ISC License
4.76k stars 297 forks source link

Problem with request-promise and request-promise-native in Jest #247

Open davazp opened 6 years ago

davazp commented 6 years ago

I have recently found that request-promise and request-promise-native can't be required at the same time in Jest. The reason is that Jest messes up with the require.cache implementation badly enough that stealthy-require is not able to return a fresh instance of request.

The second require will fail with the error

  ● Test suite failed to run

    Unable to expose method "then"

It is quite a problem because both are just indirect dependencies of many projects, so it creates weird incompatibilities.

Can we get rid of the stealthy-require magic and get a different request instance in a different way, to play nicely with Jest module system? For example, could we expose a clone() method to clone request and its prototype instead?

This affects at least to request-promise, request-promise-native and request-promise-any.

I have prepared a minimal repository to reproduce the problem: https://github.com/davazp/request-promise-issue

If needed, I can work on a pull request for this.

ptbrowne commented 6 years ago

Do you have a temporary solution ?

davazp commented 6 years ago

@ptbrowne Kind of. The problem happened to me between a private project and jsdom. The latter uses request-promise-native.

To work around it, I added "resetModules": true to the jest.config.json file, and then I defined a jsdom.js file in my project with the content:

jest.resetModules()
module.exports = require('jsdom')

Then I require this local file instead of the real jsdom. Unfortunately, jest.resetModules() cleans the cache for every module. I tried to require jsdom at the very last to minimize more issues with other modules.

This should be easy to adapt to other modules you have conflicts with. I hope it helps!

ptbrowne commented 6 years ago

Thanks for the answer. Finally I resorted to jest.mockModule('request-promise-native').

AJRdev commented 6 years ago

With the new version of Jest, you should use jest.mock('request-promise-native') instead of jest.mockModule

Alahel commented 6 years ago

I encountered the same problem, and ended with the options of jest jest.json

"setupFiles": [
    "jest.setup.js"
  ]

and use a file jest.setup.js with

jest.mock('request-promise-native');
Sytten commented 5 years ago

I just hit a similar issue when requiring request and request-promise-native. The fix worked for me too.

Rolilink commented 5 years ago

I am having the same issue, got a private package using request and request-promise-native and the project depends on request-promise-native it throws the error

 FAIL  lib/async/precommission.test.js
  ● Test suite failed to run

    Unable to expose method "then"

       7 | var _ = require('lodash');
       8 | 
    >  9 | var Update    = require('@privateOrg/update');
         |                 ^
      10 | 
      11 | var dbOptions = {
      12 |   runValidators: true,

      at Object.plumbing.exposePromiseMethod (node_modules/request-promise-core/lib/plumbing.js:140:19)
      at Object.<anonymous>.module.exports (node_modules/request-promise-core/configure/request2.js:57:83)
      at Object.<anonymous> (node_modules/@privateOrg/api/node_modules/request-promise/lib/rp.js:28:1)
      at Object.<anonymous> (node_modules/@privateOrg/api/index.js:4:10)
      at Object.<anonymous> (node_modules/@privateOrg/update/index.js:6:11)
      at Object.require (lib/discovery/handlers/jip/common/index.js:9:17)
      at Object.require (lib/discovery/handlers/jip/fixtures/index.js:9:33)
      at Object.require (lib/discovery/handlers/index.js:4:15)
      at Object.require (lib/models/device.js:6:16)
      at Object.require (lib/async/precommission.js:6:16)
      at Object.require (lib/async/precommission.test.js:1:23)

tried using

jest.mock('request-promise-native');
jest.mock('request-promise');
jest.mock('request-promise-core');

last one throws TypeError: Cannot read property 'exposePromiseMethod' of undefined I am pretty confused and don't know what to do.

Rolilink commented 5 years ago

I fixed this just mocking the whole private package, don't know if this is the best approach but it worked for me.

mattfff commented 5 years ago

Also, note that this can happen if you have multiple versions of request-promise installed, such as due to differing versions in indirect dependencies. If you are seeing this error and you are not trying to use request-promise and request-promise-native together (I'm only using request-promise) make sure you don't have multiple versions installed.

kevinburke commented 5 years ago

Has an upstream issue been filed with Jest? It's their module resolver that's broken, seems like they should produce a fix.

Benno007 commented 5 years ago

Also, note that this can happen if you have multiple versions of request-promise installed, such as due to differing versions in indirect dependencies. If you are seeing this error and you are not trying to use request-promise and request-promise-native together (I'm only using request-promise) make sure you don't have multiple versions installed.

Thanks @mattfff , I noticed in our monorepo we had request-promise in both root node_modules (4.2.2) and one of our package's node_modules (4.2.4). Fixing the sub-package to 4.2.2 fixed the issue for me.

matschik commented 5 years ago

My Jest tests are always failing because of it...

● Test suite failed to run

    Unable to expose method "then"

Solution @Benno007 does not fit for me because my function does not work at all if I downgrade it.

luke-robertson commented 5 years ago

Thanks, fixed it like this :

Screen Shot 2019-07-15 at 10 32 50

vankien96 commented 5 years ago

Also, note that this can happen if you have multiple versions of request-promise installed, such as due to differing versions in indirect dependencies. If you are seeing this error and you are not trying to use request-promise and request-promise-native together (I'm only using request-promise) make sure you don't have multiple versions installed.

Thank @mattfff. I have this issue because I install request-promise and jsdom. I try to use request-promise-native instead of request-promise and this error go away.

rodrigoreis22 commented 4 years ago

I'm also having this problem.. while jest.mock('request-promise'); works for one test case, it breaks some other tests that use the request-promise module. Any long term solutions here?

hems commented 4 years ago

I encountered the same problem, and ended with the options of jest jest.json

"setupFiles": [
    "jest.setup.js"
  ]

and use a file jest.setup.js with

jest.mock('request-promise-native');

That does not solve the issue, at least for me, for instance on a project i'm working we actually let the "post" from "request-promise" happen, as in: we do not mock request-promise.

We let the post or get method actually happen, and then we capture and mock the http responses at "network level" using the powerful https://github.com/nock/nock library, which can record the http requests, create the fixtures, replay the fixtures, debug them, etcs.

It's much more productive and realistically using nock than writing mocked responses and functions for http calls using request-promise.

antonku commented 4 years ago

I've published jest-transform-stealthy-require module to address this issue, the README contains usage examples: https://github.com/antonku/jest-transform-stealthy-require

Hope it helps

hems commented 4 years ago

Hope it helps

this seem to have worked, i can now keep mocking my http requests using https://github.com/nock/nock which i believe is much better than re-writing some methods using sinon an the likes.

EDIT: i can confirm this actually solves things exactly as they are supposed to be solved for us. thank you so much, it saved a lot of headaches on our side. we were stuck working around this very annoying problem for weeks.

antonku commented 4 years ago

i can confirm this actually solves things exactly as they are supposed to be solved for us. thank you so much, it saved a lot of headaches on our side. we were stuck working around this very annoying problem for weeks.

@hems I am happy to hear that it worked out well. Thank you very much for the feedback!

smber1 commented 4 years ago

I've published jest-transform-stealthy-require module to address this issue, the README contains usage examples: https://github.com/antonku/jest-transform-stealthy-require

Hope it helps

Mocking request-promise-native wasn't an option for us as it was a core to the impl under test. This library worked perfectly! Thanks so much mate!

antonku commented 4 years ago

@smber1 Thanks for reaching out! Happy to know it was helpful.

luk492 commented 3 years ago

Adding just

jest.mock('request-promise-native');

didn't fix my issue, but adding this did, so maybe it could help someone else too.

jest.mock('request-promise-native', () => ({ options: { request: {} }, }));