Jest + Type 脚本 + 绝对路径(baseUrl)给出错误: 无法找到模块

我正在设置一个配置,以便在一个 create-response-app + type escript 应用程序(我已经被弹出来了)中运行我的测试。我用的是 jest + 酵素。在 tsconfig.json 中,我设置了 baseUrl='./src',这样在导入模块时就可以使用绝对路径。例如,在我的一个文件中,这是一个典型的 import 语句:

import LayoutFlexBoxItem from 'framework/components/ui/LayoutFlexBoxItem';

您可以看到路径是绝对的(来自/src 文件夹) ,而不是相对的。 当我在调试模式(yarn start)下运行时,这种方法可以很好地工作

但是当我运行我的测试(yarn test)时,我得到这个错误:

 Cannot find module 'framework/components/Navigation' from 'index.tsx'

所以看起来 jest 无法解析这个绝对路径,尽管我已经在 tsconfig.json 中设置了它。这是我的 tsconfig.json:

{
"compilerOptions": {
"outDir": "dist",
"module": "esnext",
"target": "es5",
"lib": ["es6", "dom"],
"sourceMap": true,
"allowJs": true,
"jsx": "react",
"moduleResolution": "node",
"rootDir": "src",
"forceConsistentCasingInFileNames": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noImplicitAny": true,
"strictNullChecks": true,
"suppressImplicitAnyIndexErrors": true,
"noUnusedLocals": true,
"baseUrl": "./src"
},
"exclude": [
"node_modules",
"build",
"dist",
"config",
"scripts",
"acceptance-tests",
"webpack",
"jest",
"src/setupTests.ts"
]
}

现在我可以看到在我的项目的根目录下有一个生成的 tsconfig.test.json。这是用于测试的 ts 配置。以下是它的内容:

{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "commonjs"
}
}

如您所见,这里的“模块”是 commonjs,而在缺省配置中它是 esnext。这是原因之一吗?

是否有人能够用 Jest 和绝对路径对他的打印稿项目进行单元测试?还是已知的窃听器?由于我已经从默认配置中弹出,是否有一些设置要放入我的 webpack 配置中?

谢谢你的意见和建议。

94580 次浏览

您可能需要 jest 配置的 moduleNameMapper特性。 它将自定义导入命名空间映射到实际的模块位置。

点击这里查看官方文件:

Https://facebook.github.io/jest/docs/en/configuration.html#modulenamemapper-object-string-string

我也在为同样的问题苦苦挣扎,事实证明,一个简单的改变似乎就能解决问题。

我刚刚更新了 jest.config.js中的 moduleDirectories字段。

之前

moduleDirectories: ['node_modules']

之后

moduleDirectories: ['node_modules', 'src']

希望能有帮助。

下面是我如何让模块 NameMapper 工作的。

在我的 tsconfig 中使用以下配置:

    "paths": {
"@App/*": [
"src/*"
],
"@Shared/*": [
"src/Shared/*"
]
},

下面是模块 Name Mapper:

"moduleNameMapper": {
"@App/(.*)": "<rootDir>/src/$1",
"@Shared/(.*)": "<rootDir>/src/Shared/$1"
}

我已经使用了 React with Type 脚本,我从 npm test中删除了 react-scripts-ts test --env=jsdom,并添加了 jest --watch作为我的默认测试,在我按照这些说明 https://basarat.gitbooks.io/typescript/docs/testing/jest.htmljest.config.js添加到我的项目之后

我使用了@Antonie Laffake 提到的配置(添加/编辑属性 moduleDirectories: ['node_modules', 'src']) ,它工作得非常完美。

我也遇到过类似的问题。我希望这能帮助你们中的一些人腾出时间。

我的问题是:

  • 使用具有类型脚本的 create-response-app
  • 使用绝对路径(src/MyComp)将组件导入其他组件(例如 App.tsx)
  • 它正在编译/运行/构建
  • 没有正在测试

我发现错误是由 NODE _ PATH 的不同值引起的。所以我把它设置成运行测试。

我在这里重新创建了问题和修复: https://github.com/alessandrodeste/...

我不确定这是否会给测试带来副作用,如果你有反馈请告诉我;)

在您进行更改之后,请不要忘记重新启动您的测试监视器。

  "jest": {
"moduleDirectories": [
"node_modules",
"src"
],
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"roots": [
"src"
],
"testRegex": ".spec.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"coverageDirectory": "../coverage",
"testEnvironment": "node",
"moduleNameMapper": {
"src/(.*)": "<rootDir>/src/$1"
}
}

开玩笑,这个问题完全可以解决!
Https://kulshekhar.github.io/ts-jest/docs/getting-started/paths-mapping#jest-config-with-helper
像这样修改 jest.config.js:

    const { pathsToModuleNameMapper } = require('ts-jest/utils');
const { compilerOptions } = require('./tsconfig.json');
module.exports = {
// preset is optional, you don't need it in case you use babel preset typescript
preset: 'ts-jest',
// note this prefix option
moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, /* { prefix: '<rootDir>/' } */)
}

正如许多人指出的那样,jest.config.js中的 moduleNameMapper需要定义 tsconfig.json中指定的路径。例如,如果在 tsconfig.json中有如下定义的路径

// tsconfig.json
{
...
"baseUrl": "src",
"paths": {
"@alias/*": [ 'path/to/alias/*' ]
}
...
}

那么您的 jest.config.js需要在 moduleNameMapper中以下列格式提供这些路径:

// jest.config.js
module.exports = {
'roots': [
'<rootDir>/src'
],
'transform': {
'^.+\\.tsx?$': 'ts-jest'
},
'moduleNameMapper': {
'@alias/(.*)': '<rootDir>/src/path/to/alias/$1'
}
};

有了这个,我们就可以改进我们的 jest.config.js来自动转换 tsconfig.json中定义的路径。下面是 主要代码片段:

// jest.config.js


function makeModuleNameMapper(srcPath, tsconfigPath) {
// Get paths from tsconfig
const {paths} = require(tsconfigPath).compilerOptions;


const aliases = {};


// Iterate over paths and convert them into moduleNameMapper format
Object.keys(paths).forEach((item) => {
const key = item.replace('/*', '/(.*)');
const path = paths[item][0].replace('/*', '/$1');
aliases[key] = srcPath + '/' + path;
});
return aliases;
}


const TS_CONFIG_PATH = './tsconfig.json';
const SRC_PATH = '<rootDir>/src';


module.exports = {
'roots': [
SRC_PATH
],
'transform': {
'^.+\\.tsx?$': 'ts-jest'
},
'moduleNameMapper': makeModuleNameMapper(SRC_PATH, TS_CONFIG_PATH)
};

包裹 Json中将以下内容添加到我的 jest 配置中,为我解决了这个问题。

 "moduleDirectories": [
"node_modules",
"src"
]

以下是对我有效的方法:

npm i -D jest typescript
npm i -D ts-jest @types/jest
npx ts-jest config:init

然后在 jest.config.js 中,这是我的设置

module.exports = {
preset: "ts-jest",
testEnvironment: "node",
modulePaths: ["node_modules", "<rootDir>/src"],
};

在我的情况下,我没有任何 pathstsconfig.json,但我有 baseUrl设置为 src

如果你在 Monorepo 遇到这种情况,这就是解决我问题的方法:

jest.config.js内部

roots: ["<rootDir>packages"],
moduleNameMapper: {
'@monopre/(.+)$': '<rootDir>packages/$1/src',
},

假设你在 tsconfig.json

"paths": {
"@monopre/*": ["packages/*/src"],
}

对于那些使用绝对路径但不使用命名映射的人来说,这对我很有用:

# jsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
}
}

# jest.config.js
const config = {
moduleDirectories: ['node_modules', '<rootDir>'],
};

使用 Svelte Kit,我的解决方案是:

import { readFileSync } from 'fs';
import pkg from 'ts-jest/utils/index.js';
const { pathsToModuleNameMapper } = pkg;


const { compilerOptions } = JSON.parse(readFileSync('./tsconfig.json'))


export default {
preset: 'ts-jest',
testEnvironment: 'node',
testMatch: ['<rootDir>/**/*.test.ts'],
testPathIgnorePatterns: ['/node_modules/'],
coverageDirectory: './coverage',
coveragePathIgnorePatterns: ['node_modules'],
globals: { 'ts-jest': { diagnostics: false } },
transform: {},
moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, { prefix: '<rootDir>/' }),
}

对我来说,我只需要加上

"modulePaths": ["<rootDir>/src"],

我的 jest.config.js文件。

修改 moduleDirectories的答案如下导致了这个错误:

Jest encountered an unexpected token


Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.


Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration.


By default "node_modules" folder is ignored by transformers.


Here's what you can do:
• If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/ecmascript-modules for how to enable it.
• To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
• If you need a custom transformation specify a "transform" option in your config.
• If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.


You'll find more details and examples of these config options in the docs:
https://jestjs.io/docs/configuration
For information about custom transformations, see:
https://jestjs.io/docs/code-transformation

使用:

modulePaths: ["node_modules", "<rootDir>/src"],

从读取 那些文件看来,这是一个额外目录的列表,因此 node_modules是不必要的。

使用最佳实践的解决方案

出现此错误是因为在使用 Jest 时在 TypeScript/Nest.js/Angular 项目的 import语句中使用了绝对路径。使用 moduleDirectoriesmoduleNameMapper选项修复它可能会暂时解决您的问题,但它会造成其他包(如此 打字机问题)的问题。另外,Nest.js 框架 暗示的创建者认为使用绝对路径是一种不好的做法。TypeScript 的官方 文件建议对我们自己的模块使用相对路径。还有一个 ESLint 规则解释了为什么应该避免绝对路径。


绝对路径与相对路径

import绝对路径的声明如下:

import { AuthService } from 'src/auth/auth.service'

import相对路径的声明如下:

import { AuthService } from '../auth/auth.service'

VS 代码设置

VS 代码默认使用绝对路径,如上所示,当我们使用代码完成或 Command/Ctrl + .自动导入时。我们需要更改此默认设置以使用相对路径。

转到 VS 代码设置并搜索一个设置: Import Module Specifier。将其从 shortest更改为 relative

从现在开始,VS Code 将使用相对路径自动导入。


修复项目中的导入

现在在项目文件中,在导入中查找与上面的示例类似的绝对路径并删除它们。您将看到您删除的包的错误。只需使用自动导入建议并将它们导入回来。这次将使用相对路径导入它们。根据项目的大小,这个步骤可能很乏味,但是从长远来看是值得的。


希望能成功,干杯!

如果已经安装了 ts-jest,那么可以使用名为 模块名称映射器的 util 函数将 tsconfig.json 中的路径转换为 jest.config 文件:

我的 jest.config.js 文件:

const { join } = require('path');
const { pathsToModuleNameMapper } = require('ts-jest')
const { compilerOptions } = require('./tsconfig.json')


/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */


module.exports = {
rootDir: __dirname,
setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
setupFiles: ['<rootDir>/src/config/env.ts'],


collectCoverageFrom: ["<rootDir>/src/modules/**/*UseCase.ts"],
coverageProvider: "v8",
coverageThreshold: {
global: {
lines: 40
}
},


bail: true,
clearMocks: true,
displayName: 'unit-tests',
testMatch: ["<rootDir>/src/modules/**/*.spec.ts"],


preset: 'ts-jest',
testEnvironment: 'node',


modulePaths: ["<rootDir>/src"],
moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, {
prefix: join('<rootDir>', compilerOptions.baseUrl)
})
};

我在使用 StitchesJS 时遇到了同样的问题,没有找到模块,解决方案是把它放到 jest.config.js 中

  moduleNameMapper: {
"stitches.config": "<rootDir>/node_modules/@stitches/react/dist/index.cjs"}

您可以根据需要的模块进行调整。

如果您有一个 monorepo,其中每个项目都有自己的从基(根) Tsconfig.json扩展的 Tsconfig.json,并且每个项目都有自己的 Jest.config.js,并且您从项目根启动测试,那么您应该将您的 moduleNameMapper 添加到项目 中的 Jest.config.js

艾格。

backend/
- tsconfig.json
- jest.config.js


frontend/
- tsconfig.json
- jest.config.js


/
- tsconfig.json
- jest.config.js

你的问题是在玩笑测试 前/中,它有这样一个 tsconfig:

{
"compilerOptions": {
"jsx": "react-native",
"paths": {
"src/*": ["client/src/*"]
},
"noEmit": true
},
"extends": "../tsconfig.json"
}


然后把它添加到 jest.config 中应该可以解决这个问题:

  moduleNameMapper: {
'src/(.*)': '<rootDir>/client/src/$1',
},

所以总的来说,大概是:

module.exports = {
testEnvironment: 'node',
testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$',
testPathIgnorePatterns: ['cdk.out/'],
transform: {
'^.+\\.tsx?$': 'ts-jest',
},
modulePathIgnorePatterns: ['tests'],
globals: {
'ts-jest': {
isolatedModules: true,
},
},
moduleNameMapper: {
'src/(.*)': '<rootDir>/client/src/$1',
},
};


有点显而易见,但是加上这个答案,因为我在其他答案中没有看到这种情况,并且 monorepos 仍然是一个东西:)

有点像一个老线程,但是最近,除了在 jest.config.js上的 "moduleNameMapper":tsconfig.json上的 "paths":上添加路径的其他建议之外,我还必须在 包括上添加我的测试文件夹,以便编译器能够识别它。

所以在 tsconfig.json上,加上:

...
"include": ["src", "__tests__"],
...

希望有人能在谷歌上找到