如何解决在运行Jest测试时出现的“Cannot use import statement outside a module”的问题?

335

我有一个使用TypeScript、Jest、Webpack和Babel构建的React应用程序(不使用Create React App)。在尝试运行yarn jest时,我收到以下错误:

jest error

我尝试过删除所有软件包并重新添加它们,但这并不能解决问题。我查看了类似的问题和文档,但仍然不理解其中的某些内容。我甚至按照另一个从头设置此环境的指南,仍然在我的代码中遇到了这个问题。

依赖项包括...

"dependencies": {
  "@babel/plugin-transform-runtime": "^7.6.2",
  "@babel/polyfill": "^7.6.0",
  "babel-jest": "^24.9.0",
  "react": "^16.8.6",
  "react-dom": "^16.8.6",
  "react-test-renderer": "^16.11.0",
  "source-map-loader": "^0.2.4"
},
"devDependencies": {
  "@babel/core": "^7.6.0",
  "@babel/preset-env": "^7.6.0",
  "@babel/preset-react": "^7.0.0",
  "@types/enzyme": "^3.9.2",
  "@types/enzyme-adapter-react-16": "^1.0.5",
  "@types/jest": "^24.0.13",
组件的导入行...
import * as React from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import HomePage from "./components/pages";
import {
  Footer,
  Header,
  Navigation,
} from "./components/shared";

测试文件包含用于验证软件或系统功能的测试套件和用例。 在开发过程中,经常需要运行测试以确保代码或产品符合预期。 测试文件还可以包括结果报告和其他有关测试执行的信息。

import * as React from "react";
import * as renderer from "react-test-renderer";
import App from "../App";

it("Renders the Footer correctly", () => {
  const tree = renderer
    .create(<App />)
    .toJSON();
  expect(tree).toMatchSnapshot();
});

我希望在我的组件中使用具名导入而不会导致我的测试失败。如果我只在整个解决方案中使用默认导入,似乎可以修复此问题,但我更希望不采用这种方法。


2
顺便提一下,当我为Jest设置自定义TestSequencer时遇到了这个问题,我只是在这个文件中切换到使用require而不是import - Joshua Pinter
这可能是由于环境变量问题引起的。请参见我的其他相关答案:https://dev59.com/Aqfja4cB1Zd3GeqP2uMm#63749183 - Bence Szalai
如果有人目前遇到这个问题,这个解决方案在我的情况下修复了这个错误。https://github.com/react-hook-form/resolvers/issues/396#issuecomment-1114248072 - diemondtank
@diemondtank,这显然是一个特定于包(hookform)的解决方案。我在resolver.js中看到了if (pkg.name === '@hookform/resolvers') - 丶 Limeー来夢 丶
这似乎是Jest中的一个错误引起的:https://github.com/facebook/jest/issues/8365 - Elias Zamaria
28个回答

264
我遇到了同样的问题(也是使用Babel、Typescript和Jest),这让我疯狂了几个小时!
最后,我为测试创建了一个新的babel.config.js文件。我有一个很大的.babelrc文件,无论我怎么修改它,jest都无法识别它。主应用程序仍然使用.babelrc文件,因为它会覆盖babel.config.js文件。
我采取的步骤:
安装jest、ts-jest、babel-jest和@babel/preset-env:
npm i jest ts-jest babel-jest @babel/preset-env

添加 babel.config.js 文件(仅供 jest 使用)
module.exports = {presets: ['@babel/preset-env']}

jest.config.js中进行更新为:
module.exports = {
  preset: 'ts-jest',
  transform: {
    '^.+\\.(ts|tsx)?$': 'ts-jest',
    '^.+\\.(js|jsx)$': 'babel-jest',
  }
};

package.json

  "scripts": {
    "test": "jest"

3
谢谢!这个方法非常有效。我需要将我的 .babelrc 转换为 babel.config.js,但最终一切都很顺利! - Steph M
20
工作时,请不要忘记同时安装@babel/preset-env - Moso Akinyemi
2
非常感谢,这个问题也困扰了我很久,你的解决方案只需要几秒钟就能实现,正如其他人所说 - 它非常有效。 - Nick Taras
1
这对我有用。在 jest.config.js 中设置 preset: 'ts-jest' 是关键。 - Juan David Robles
1
我遇到了一个依赖于另一个依赖的问题;在上述步骤之上,不得不在 jest.config.ts 中添加 transformIgnorePatterns: ["/node_modules/(?!formidable)"] - Akber Iqbal
显示剩余7条评论

95

使用 Babel 转译这些 JS 模块,你就可以使用 es6 编写测试了。

安装 Babel/preset-env

npm i -D @babel/preset-env

创建带有 preset 的 Babel 配置文件

//babel.config.js
module.exports = {presets: ['@babel/preset-env']}

5
感谢您的清晰简洁的要求!针对我的项目派生自 tsdx ,这个技巧很有用(与 @babel/preset-react 一起使用)。 - Jason R Stevens CFA
4
谢谢,这解决了我的问题。 - Victor Karangwa
5
“npm i -D @babel/preset-env” 给我报错了。需要使用 “npm i --save-dev @babel/preset-env”。 - guero64
4
这个很完美!(请注意:如果您选择这种方法,package.json中不能使用"type":"module"。) - skladany
这个几乎完美地工作了。不过需要使用 babel.config.json,内容如下:{ "presets": ["@babel/preset-env"] } - Chantz
显示剩余2条评论

36

我通过将.babelrc文件迁移到babel.config.js来解决了这个问题!令人震惊。


3
这是我的问题!忘记复制 babel 配置了!谢谢 :) - Ryall

27

我通过在package.json的运行命令后面简单地添加这个模式来解决了它。

{
  "scripts": {
    ...
    "test": "react-scripts test --transformIgnorePatterns 'node_modules/(?!my-library-dir)/'"
    ...

然后,只需运行npm test


谢谢。对我来说很有效。但这个命令到底是做什么的?是一种转换吗? - undefined

18

为了以后参考,

在阅读洛根·肖梅克的答案后,我使用以下Jest配置解决了这个问题。

module.exports = {
  verbose: true,
  setupFilesAfterEnv: ["<rootDir>src/setupTests.ts"],
  moduleFileExtensions: ["js", "jsx", "ts", "tsx"],
  moduleDirectories: ["node_modules", "src"],
  moduleNameMapper: {
    "\\.(css|less|scss)$": "identity-obj-proxy"
  },
  transform: {
    '^.+\\.(ts|tsx)?$': 'ts-jest',
    "^.+\\.(js|jsx)$": "babel-jest",
    "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/file.js",
  }
};

4
我的 Jest 配置只缺少了 **moduleDirectories: ["node_modules", "src"]**,我添加了这个配置后一切都按照预期开始工作了。谢谢。 - ZAD
谢谢,moduleFileExtensions: ["ts", "tsx", "js", "jsx"] 在我的情况下解决了问题。 - Zeid Al-Rashwani
谢谢,moduleFileExtensions: ["ts", "tsx", "js", "jsx"] 在我的情况下解决了问题。 - undefined

13

如果您正在使用babel 6,请尝试以下方法:

  1. babel.config.js的插件部分中添加@babel/plugin-transform-modules-commonjs

或者

  1. 对于我的情况,导入问题是由于react文件被删除所致,通过将其添加到transformIgnorePatterns中解决

"transformIgnorePatterns": ["/node_modules/(?!react-file-drop)"]


1
在我的情况下,只有 transformIgnorePatterns: ['/node_modules/(?!react-hls-player)'], 有所帮助。 - Tomas Dermisek
哇,步骤1解决了我的问题,使用Babel 7。太神奇了。这里有很多可能的解决方案,但这是我选的一个。 - Steve Bennett

12
解决方案:我的命名导入是来自index.js文件,而我相信ts-jest需要它们作为index.ts文件(我正在使用Typescript)。如果其他人也遇到了这个错误,不妨检查一下你的文件扩展名是否出错。
不幸的是,我在这方面浪费了很多时间,但我学到了很多关于webpack配置和Babel的知识。

19
你是如何解决其他模块中 index.js 的问题的? - Teodor Ciuraru
2
我花了两个小时解决这个错误。看完这个答案后,我现在想哭 T_T。我正在从 JavaScript 迁移到 TypeScript,但我忘记将“.js”重命名为“.ts”。 - aldenn
为什么导入必须只来自于 index.ts?我有很多 TS 文件,我直接使用 export class ABC,我希望它能够工作。 :-( - ankush981
也很好奇你最终是如何解决这个特定问题的。 - Christopher

5
在Node实验性特性中,使用--experimental-vm-modules将你的test脚本添加到package.json中。这样,你就不需要babel或其他依赖项。
示例:
"test": "NODE_OPTIONS='--experimental-vm-modules --experimental-specifier-resolution=node' jest"

如果你遇到这个错误:zsh: command not found: jest,尝试使用node并像这样传递 jest.js
"test": "NODE_OPTIONS='--experimental-vm-modules --experimental-specifier-resolution=node --trace-warnings' node node_modules/jest/bin/jest.js --detectOpenHandles"

对我来说,以下内容:"test": "NODE_OPTIONS='--experimental-vm-modules --experimental-specifier-resolution=node' jest"并不能修复错误,而是将错误从SyntaxError: Cannot use import statement outside a module更改为Must use import to load ES Module: - Matthew Strasiotto
在您的标志中添加--experimental-modules - Adrian Escutia Soto

4

我很惊讶没有一个答案提供了一种优雅的解决方案:

jest.config.js

module.exports = {
  ...,
  globals: {
    "ts-jest": {
      isolatedModules: true,
    },
  },
};

这种方式将每个文件单独编译,从而避免了“无导出项”问题。


谢谢,我也很惊讶为什么没有人很快点赞你的答案,但是你解决了我的问题! - Fabien Salles
2
对我来说不太优雅...无法工作 - Tim Rutter
4
我尝试了这个,但是出现了“(WARN) 在globals下定义ts-jest配置已经被弃用”的警告,并且它也没有解决问题。 - Ryan

4

我发现这个错误可能是在尝试加载一个适用于浏览器的依赖项时触发的,因此无法与jest(node)一起使用。

对于@zip.js/zip.js库,我解决这个问题遇到了很多麻烦。但是我可以这样做:

这是我的jest.config.js文件。根据您的需要进行调整。这里的诀窍是moduleNameMapper,它将使所有对zip.js的导入指向我在根文件夹中创建的__mocks__/@zip.js/zip.js文件。

export default {
  preset: 'ts-jest',
  testEnvironment: 'node',
  moduleNameMapper: {
    '@zip.js/zip.js': '<rootDir>/__mocks__/@zip.js/zip.js',
  },
}

这是我在 <rootDir>/__mocks__/@zip.js/zip.js 文件中的内容:

module.exports = {}

在我的情况下,正是关于 AXIOS 的这个问题。通过设置 transformIgnorePatterns "node_modules/(?!axios)/" 来解决了它。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接