Невозможно использовать модуль uuid при запуске тестов с Jest

У меня очень простой Node.js (12.16.3) приложение, использующее Express 4.17.1. Я пытаюсь использовать Jest26.0.1 для запуска набора тестов, но то же самое не удается из-за некоторых проблем с uuid модуль (версия 8.1.0) используется во всем проекте:

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

> node-guacamole@0.3.0 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.

Я импортирую модуль вроде: import { v4 } from "uuid"; а с другой стороны, приложение работает успешно:

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

> node-guacamole@0.3.0 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

... и все работает нормально. Я озадачен... Я не понимаю, почему это не удается только с Jest. Что еще мне нужно сделать, чтобы он заработал?

4 ответа

Решение

TL;DR: Jest еще не поддерживает эту область "exports" в package.json.

Проблема в том, что Node.js использует версию ESM, поскольку он понимает поле "экспорт" в package.json, но поскольку Jest еще не поддерживает его, Jest использует "основное" поле в package.json, которое экспортирует версию CommonJS.. См. Соответствующий раздел package.json:

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

Что это делает:

  1. Экспорт по умолчанию main, как всегда.
  2. Если exports понимается, что перезаписывает "main" экспорт
  3. Экспорт по умолчанию "." определяет требование и импорт, поэтому Node.js использует "импорт"
  4. Но проблема в том, что проблема Jest указывает на то, что он еще не понимает "Экспорт пакетов", поэтому Jest пытается загрузить файл CommonJS (из "main"), в то время как Node.js загружает файл ESM.

Я нашел ту же проблему и задокументировал ее здесь. В качестве эксперимента это должно работать с Jest:

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

Но с Node.js это не сработает, поскольку UUID не определяет экспорт по умолчанию.

У вас есть два реалистичных варианта:

  • Подождите, пока Jest поддержит экспорт пакетов, они отлично справляются с поддержкой ESM, и я не думаю, что это займет много времени.
  • Проголосуйте за мое предложение вuuid пакет, который они также экспортируют по умолчанию, например export default uuid в wrapper.mjs, что позволит вам использовать приведенный выше фрагмент.

Проблема заключается в несовместимости версий между Jest а также uuid. uuid версии 8 поддерживает собственные модули ES, а версия Jest 26 - нет.

Вы всегда можете использовать uuid версии 3.xx со следующим синтаксисом:

const uuid = require('uuid/v4');
uuid(); // ⇨ '9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d'

Импорт пакета uuid можно переопределить с помощью макета, который устраняет ошибку.

      // In your .test.tsx file
jest.mock('uuid', () => ({ v4: () => '00000000-0000-0000-0000-000000000000' }));

Мой ответ является второстепенным, но я подозреваю, что природа проблемы связана с синтаксисом импорта import { v4 } from "uuid".

Например, там есть соответствующее предупреждение:

(узел:74672) ExperimentalWarning: Загрузчик модуля ESM является экспериментальным.

Может быть тонкое несоответствие между вашим обычным комплектом JavaScript и способом загрузки модулей в систему Jest. Следовательно, решение может включать добавление в файл конфигурации, чтобы Jest мог понять синтаксис ESM. Например, вот интересный ответ: ts-jest не распознает импорт es6

Связанный MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import

Оператор импорта не может использоваться во встроенных сценариях, если такой сценарий не имеет type="module". Импортированные привязки называются живыми привязками, потому что они обновляются модулем, экспортировавшим привязку.

В качестве теста вы можете увидеть, имеет ли значение его импорт следующим образом:

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

или

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

Я бы не рекомендовал использовать это, если он работает, но он должен сузить его до определенного синтаксиса.

Проблема связана с этим файлом здесь: https://github.com/uuidjs/uuid/blob/master/src/index.js, на мой взгляд, потому что файл является сквозным (примерно аналогично символической ссылке), поэтому Jest пытается импортировать экспорт из экспорта.

Вероятно, это связано с Babel и отсутствием приспособлений для трансформации. Его правильное исправление должно облегчить целый класс проблем, связанных с "загрузчиком ESM".

Другие вопросы по тегам