jest icon indicating copy to clipboard operation
jest copied to clipboard

feat(runtime): add jest.mockModule

Open SimenB opened this issue 4 years ago • 23 comments

Summary

A start on #10025. Lots of stuff missing

  • [ ] Manual mocks (i.e. mocks from __mocks__ directories)
  • [ ] Docs
  • [ ] mocks without factories - not sure how to load them and determine types etc.
  • [ ] behavior when calling mock and doMock on an ES Module, and mockImport on CJS. I think it should throw?
  • [ ] jest.importActual
  • [ ] handle concurrent imports of mocks. Right now there's a race condition since it doesn't populate the cache immediately upon creating a mock. Need some sort of mutex

Test plan

I'll add more tests as we go

SimenB avatar Dec 23 '20 15:12 SimenB

@SimenB as you said in https://github.com/facebook/jest/issues/9430#issuecomment-750349327 this is needed to implement the exports feature: do you know of any workaround meanwhile?

damianobarbati avatar Jan 21 '21 10:01 damianobarbati

@SimenB as you said in #9430 (comment) this is needed to implement the exports feature: do you know of any workaround meanwhile?

This is not related to exports, what I meant is that that is the final missing piece. Not dependent on the feature in this PR. You can track exports support in #9771

SimenB avatar Feb 09 '21 09:02 SimenB

Codecov Report

Merging #10976 (eac6673) into master (a03b6fe) will decrease coverage by 0.10%. The diff coverage is 30.23%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master   #10976      +/-   ##
==========================================
- Coverage   64.28%   64.17%   -0.11%     
==========================================
  Files         308      308              
  Lines       13480    13512      +32     
  Branches     3286     3291       +5     
==========================================
+ Hits         8665     8671       +6     
- Misses       4106     4134      +28     
+ Partials      709      707       -2     
Impacted Files Coverage Δ
packages/jest-resolve/src/index.ts 45.96% <0.00%> (ø)
packages/jest-runtime/src/index.ts 53.15% <30.95%> (-1.70%) :arrow_down:

Continue to review full report at Codecov.

Legend - Click here to learn more Δ = absolute <relative> (impact), ø = not affected, ? = missing data Powered by Codecov. Last update a03b6fe...eac6673. Read the comment docs.

codecov-io avatar Mar 14 '21 16:03 codecov-io

what's the state of this?

piranna avatar Jun 02 '21 10:06 piranna

I currently don't have the motivation to work on this feature, so it's in hiatus until I get back to it. Others are of course free to pick it up (missing stuff is mostly laid out in the OP from what I remember) 🙂 I can rebase this, tho

SimenB avatar Jun 03 '21 08:06 SimenB

Codecov Report

Merging #10976 (9af07a9) into master (90d6908) will decrease coverage by 0.01%. The diff coverage is 14.28%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master   #10976      +/-   ##
==========================================
- Coverage   68.93%   68.92%   -0.02%     
==========================================
  Files         312      312              
  Lines       16398    16401       +3     
  Branches     4749     4750       +1     
==========================================
  Hits        11304    11304              
- Misses       5067     5070       +3     
  Partials       27       27              
Impacted Files Coverage Δ
packages/jest-runtime/src/index.ts 57.12% <14.28%> (-0.21%) :arrow_down:

Continue to review full report at Codecov.

Legend - Click here to learn more Δ = absolute <relative> (impact), ø = not affected, ? = missing data Powered by Codecov. Last update 90d6908...9af07a9. Read the comment docs.

codecov-commenter avatar Jun 03 '21 08:06 codecov-commenter

@SimenB, is there a natural point to split up this PR / what remains? For example, if __mocks__ or jest.importActual is a significant amount of work, would it be preferable for a contributor to work on the rest first and land those things in a follow up PR?

I can take a look at helping with some of this tomorrow. A rebase, if it'd be quick for you, would be appreciated (assuming there might be conflicts to be resolved that I wouldn't have the context for).

connorjclark avatar Aug 25 '21 01:08 connorjclark

@connorjclark awesome if you wanna pick this up! I just rebased it. Not sure where the best point to pick something up would be. I assume e.g. jest.importActual should be fairly self-contained and not too advanced, so should be a nice place to start 🙂

SimenB avatar Aug 27 '21 10:08 SimenB

Some of the items from above look required to merge this, but I wonder if some might be able to be moved to follow-ups? E.g. this could provide a lot of value without manual mocks support

benmccann avatar Sep 02 '21 19:09 benmccann

Sure, pretty much every point could be moved to a follow-up except possibly docs... Although one could argue docs can wait until the feature is more stabilized. I could add this as jest.unstable_mockModule or something for now to be safe, and remove the prefix when it's feature complete (and then the unstable function in the next major)

SimenB avatar Sep 07 '21 15:09 SimenB

Checking it in as jest.unstable_mockModule would be awesome and very much appreciated!

benmccann avatar Sep 07 '21 15:09 benmccann

@benmccann landed that now, will release tonight or tomorrow (cest)

SimenB avatar Sep 07 '21 16:09 SimenB

Out in https://github.com/facebook/jest/releases/tag/v27.1.1

SimenB avatar Sep 08 '21 10:09 SimenB

Thank you so much!

benmccann avatar Sep 08 '21 14:09 benmccann

@SimenB time to update https://jestjs.io/docs/ecmascript-modules , it still says "Please note that we currently don't support jest.mock in a clean way in ESM, but that is something we intend to add proper support for in the future. Follow this issue for updates."

n0mer avatar Jan 02 '22 00:01 n0mer

@n0mer if you're up for a quick PR adding a small note that'd be awesome 👍 Can link to https://github.com/facebook/jest/issues/9430#issuecomment-915109139 or something for the caveats

SimenB avatar Jan 05 '22 13:01 SimenB

Are there any plans to continue this work in the near term? @SimenB

cbouwense avatar Mar 08 '22 20:03 cbouwense

All work on ESM is paused until most issues linked in https://github.com/nodejs/node/issues/37648 are solved.

(I got a message from a developer at Google last week about movement, so hopefully that'll actually move along very soon)

SimenB avatar Mar 08 '22 21:03 SimenB

That said, happy to take PRs of course. But any personal effort is on pause until node's APIs are closer to stabilization.

SimenB avatar Mar 08 '22 21:03 SimenB

The downside to jest.unstable_mockModule() right now is that it mocks the entire module. This is maybe not a downside if that's what you're looking for. It's really falling down for me right now though, because I only want to mock a few functions from the module. This really needs a jest.importActual() to fill in that missing gap. Trying to use jest.requireActual() on an ESM of course returns a 'Must use import to load ES Module' error, which one would expect to happen. I'm getting to the point where even I might take a crack at implementing that functionality, but

a) For the past 26 years I've written in pure javascript for use in a browser environment (hence why I'm using ESM) b) Am not super familiar with TypeScript outside of Haxe because I've always been able to do what I need to in pure JavaScript (And transpiling/bundling feels dirty since it's supposed to be a runtime script) c) Am not super familiar with Node outside of using npm to pull down libraries, as the server side has always been Perl/PHP/C#/Python up until the last few years. Of course this all means that using Jest has been a ~~struggle~~ learning opportunity.

At the very least it looks like the requireActual() implementation is happening in node_modules/jest-runtime/build/index.js, and that it is basically just calling requireModule() with a flag flipped to true. The flag being flipped to true seems to only really effect if modulePath = manualMock or not, which then effects if you receive the mocked module or the actual module through ~~black magic~~ Node CJS ~~shennanigans~~ transforms.

From this I would guess that within the same file, the unstable_importModule() function (or further down the line loadEsmModule() function) could be extended to accept the same sort of flag solution. However, once we get there, there is even more Pure ES Standards Javascript to CJS/Babel transform voodoo to decipher. When I get to this point it starts bringing on too many Bad Memories of the JavaScript vs. JScript days and I get triggered and have to leave the room and have a lie down.

I guess if anybody was to take a crack at jest.importActual that's the place to do it (or wherever jest-runtime/build/index.js is actually transpiled down from). Unfortunately, it's probably not going to be me, because ironically I find pure JavaScript easy (again, 26 years of practice), but find the modern workflow bloated and overly complicated. However, I hope somebody who understands the newer way of doing things finds the research helpful, or like Simen says, we wait for Node to support the standards and it doesn't take another few years.

oldmansutton avatar Jun 09 '22 19:06 oldmansutton

The downside to jest.unstable_mockModule() right now is that it mocks the entire module. This is maybe not a downside if that's what you're looking for. It's really falling down for me right now though, because I only want to mock a few functions from the module. This really needs a jest.importActual() to fill in that missing gap. Trying to use jest.requireActual() on an ESM of course returns a 'Must use import to load ES Module' error, which one would expect to happen. I'm getting to the point where even I might take a crack at implementing that functionality, but

a) For the past 26 years I've written in pure javascript for use in a browser environment (hence why I'm using ESM) b) Am not super familiar with TypeScript outside of Haxe because I've always been able to do what I need to in pure JavaScript (And transpiling/bundling feels dirty since it's supposed to be a runtime script) c) Am not super familiar with Node outside of using npm to pull down libraries, as the server side has always been Perl/PHP/C#/Python up until the last few years. Of course this all means that using Jest has been a ~struggle~ learning opportunity.

At the very least it looks like the requireActual() implementation is happening in node_modules/jest-runtime/build/index.js, and that it is basically just calling requireModule() with a flag flipped to true. The flag being flipped to true seems to only really effect if modulePath = manualMock or not, which then effects if you receive the mocked module or the actual module through ~black magic~ Node CJS ~shennanigans~ transforms.

From this I would guess that within the same file, the unstable_importModule() function (or further down the line loadEsmModule() function) could be extended to accept the same sort of flag solution. However, once we get there, there is even more Pure ES Standards Javascript to CJS/Babel transform voodoo to decipher. When I get to this point it starts bringing on too many Bad Memories of the JavaScript vs. JScript days and I get triggered and have to leave the room and have a lie down.

I guess if anybody was to take a crack at jest.importActual that's the place to do it (or wherever jest-runtime/build/index.js is actually transpiled down from). Unfortunately, it's probably not going to be me, because ironically I find pure JavaScript easy (again, 26 years of practice), but find the modern workflow bloated and overly complicated. However, I hope somebody who understands the newer way of doing things finds the research helpful, or like Simen says, we wait for Node to support the standards and it doesn't take another few years.

I appreciate all the work done on Jest but I'm switching to vitest for first-class typescript and ESM support, including support for hoisted ESM mocks and importActual support

revmischa avatar Jun 09 '22 19:06 revmischa

This PR is stale because it has been open 1 year with no activity. Remove stale label or comment or this will be closed in 30 days.

github-actions[bot] avatar Jun 15 '23 08:06 github-actions[bot]

landed here from the jest docs, see limited support ESM pushed in 2021, thanks @SimenB https://github.com/jestjs/jest/pull/10976#issuecomment-915107516

image

there was comments on waiting for node APIs to stabilize that was 3 years ago, has ESM support not changed since then?

p.s. im new to jest but looking to mock strictly ESM .ts files

iSpeakNerd avatar May 04 '25 21:05 iSpeakNerd

What's the status of this PR?

cpojer avatar Jul 06 '25 06:07 cpojer

i think this functionality broke in jest@30 i had many tests with jest.unstable_mockModule working, and the version bump to @30 broke them (the tests failed with the unexpected result [because it ran source code instead of the mocked value to be used]; no other failures). i just marked all those tests as .skip waiting for hopefully future functionality here

skilbjo avatar Jul 07 '25 22:07 skilbjo