Can't use uuid module when running tests with Jest
Asked Answered
A

5

9

I have a very simple Node.js (12.16.3) application that uses Express 4.17.1. I'm trying to use Jest 26.0.1 to run the test suite, but the same is failing due to some issue with the uuid module (version 8.1.0) used across the entire project:

[x80486@uplink:~/Workshop/node-guacamole]$ npm run test 

> [email protected] test /home/x80486/Workshop/node-guacamole
> node --experimental-modules --experimental-vm-modules ./node_modules/.bin/jest --coverage --detectOpenHandles --forceExit --verbose

(node:71155) ExperimentalWarning: The ESM module loader is experimental.
 FAIL  src/domain/customer.test.js
  ● Test suite failed to run

    SyntaxError: The requested module 'uuid' does not provide an export named 'v4'

      at jasmine2 (node_modules/jest-jasmine2/build/index.js:228:5)

 FAIL  src/service/customer.service.test.js
  ● Test suite failed to run

    SyntaxError: The requested module 'uuid' does not provide an export named 'v4'

          at async Promise.all (index 4)
      at jasmine2 (node_modules/jest-jasmine2/build/index.js:228:5)

 FAIL  src/handler/customer.handler.test.js
  ● Test suite failed to run

    SyntaxError: The requested module 'uuid' does not provide an export named 'v4'

          at async Promise.all (index 2)
          at async Promise.all (index 7)

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------|---------|----------|---------|---------|-------------------
All files |       0 |        0 |       0 |       0 |                   
----------|---------|----------|---------|---------|-------------------
Test Suites: 3 failed, 3 total
Tests:       0 total
Snapshots:   0 total
Time:        0.63 s
Ran all test suites.

I'm importing the module like: import { v4 } from "uuid"; and on the other hand, the application runs successfully:

[x80486@uplink:~/Workshop/node-guacamole]$ npm run start:dev 

> [email protected] start:dev /home/x80486/Workshop/node-guacamole
> nodemon --experimental-modules --experimental-vm-modules ./src/main.js

[nodemon] 2.0.4
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting `node --experimental-modules --experimental-vm-modules ./src/main.js`
(node:74672) ExperimentalWarning: The ESM module loader is experimental.
2020-06-03T03:28:48.889Z [debug] - Server running at http://localhost:8080
2020-06-03T03:28:48.889Z [info] - Press CTRL-C to stop

...and everything works fine. I'm puzzled... I don't understand why this fails with Jest only. Is there something else I need to do to make it work?

Anthropomorphosis answered 3/6, 2020 at 3:36 Comment(0)
P
10

TL;DR: Jest not yet supports the field "exports" in package.json.

The problem is that Node.js uses the ESM version since it understands the "exports" field in the package.json, but since Jest does not yet support it, Jest uses the "main" field in package.json which exports the CommonJS version. See the relevant package.json section:

...
"main": "./dist/index.js",
"exports": {
  "./package.json": "./package.json",
  ".": {
    "require": "./dist/index.js",
    "import": "./wrapper.mjs"
  }
},
...

What this does it:

  1. The default export is main, as always.
  2. If the exports is understood, that overwrites the "main" exports
  3. The default exports "." defines a require and import, so Node.js uses the "import"
  4. But the problem is that the Jest issue indicates that it does not yet understand "Package Exports", so Jest is trying to load the CommonJS file (from "main") while Node.js loads the ESM file.

I found the exact same issue and documented it here. As an experiment, this should work with Jest:

import uuid from 'uuid';
const { v4 } = uuid;

But that will not work with Node.js since UUID does not define a default export.

You have two realistic options:

  • Wait until Jest supports package exports, they are doing a great job with ESM support and I don't think it will take long.
  • Upvote my proposal in the uuid package that they also export a default, like export default uuid in wrapper.mjs, which would allow you to use the snippet I put above.
Pilau answered 5/6, 2020 at 7:24 Comment(2)
I think you nail this one, because I was using that way to import the package when it failed initially, but then the application didn't work, so I went back because what I had initially (and have now) was the recommended way from the uuid developers. Thanks for the explanation!Anthropomorphosis
I think Francisco has got it right. As the maintainer of uuid I would just like to note that these are all experimental Node.js APIs (hence the --experimental-modules --experimental-vm-modules flags) and hiccups are therefore expected. I'd rather not add a default export just for the sake of working around a current flaw in jest which will be fixed very soon. That would just make testing proper pkg.exports support with jest harder.Incense
R
1

The uuid package import can be overridden with a mock which squashes the error.

// In your .test.tsx file
jest.mock('uuid', () => ({ v4: () => '00000000-0000-0000-0000-000000000000' }));
Resh answered 22/8, 2023 at 20:52 Comment(0)
E
0

The problem is version incompatibility between Jest and uuid. uuid version 8 supports native ES modules while Jest version 26 doesn't.

You can always uuid version 3.x.x with the following syntax:

const uuid = require('uuid/v4');
uuid(); // ⇨ '9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d'
Eckhart answered 4/6, 2020 at 21:39 Comment(4)
I don't fully follow that one. Jest needs to support some "ES module" features, right? Otherwise I wouldn't be even able to execute anything. If I just remove that uuid module, everything is fine. The problem is only with Jest, I can execute the application correctly while using the uuid module.Anthropomorphosis
Can you post your configuration files? What other libraries are you using with import syntax and are they working with Jest?Eckhart
Alim, what happens is that Jest does not yet supports the field "exports"; see the answer from Francisco.Anthropomorphosis
I'm using Jest 29 and uuid 9 and I'm still getting this errorBarth
G
0

Another solution is to update the jest.config.js file:

...
moduleNameMapper: {
    // Force module uuid to resolve with the CJS entry point, because Jest does not support package.json.exports. See https://github.com/uuidjs/uuid/issues/451
    "uuid": require.resolve('uuid'),
},

GitHub source

Gangster answered 14/8 at 19:2 Comment(0)
G
-1

My answer is a side note, but I suspect the nature of the issue has to do with the import syntax of import { v4 } from "uuid".

For example there is a related warning there:

(node:74672) ExperimentalWarning: The ESM module loader is experimental.

There may be a subtle incongruence between your normally-bundled JavaScript and the way the modules are loaded into the Jest system. Therefore, a solution might involve an addition into a config file--so Jest can understand ESM syntax. For example here is an interesting answer: ts-jest does not recognize es6 imports

Related MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import

The import statement cannot be used in embedded scripts unless such script has a type="module". Bindings imported are called live bindings because they are updated by the module that exported the binding.

As a test, you could see if it makes a difference importing it like this:

const { v4: uuidv4 } = require('uuid');
const v4 = uuidv4;

or

const v4 = require('uuid').v4 // might work as well

I wouldn't recommend using that if it works, but it should narrow it down to definitely that syntax.

The issue stems from this file here: https://github.com/uuidjs/uuid/blob/master/src/index.js in my opinion, because the file is a pass-through (roughly analogous to a symlink), so Jest is trying to import an export of an export.

It's probably related to Babel and a lack of transformation fixtures. Fixing it properly should alleviate the whole class of issues related to "ESM loader".

Galling answered 4/6, 2020 at 21:27 Comment(2)
So the import syntax should be fine, I'm using the application correctly; the problem arises when try to run the tests via Jest. I tried you suggestions, but since I have "type": "module" in package.json I guess it won't let me use it.Anthropomorphosis
It's a fascinating issue. Glad you got a solution for it.Galling

© 2022 - 2024 — McMap. All rights reserved.