【问题标题】:Jest + Typescript + Absolute paths (baseUrl) gives error: Cannot find moduleJest + Typescript + 绝对路径(baseUrl)给出错误:找不到模块
【发布时间】:2018-10-14 17:36:49
【问题描述】:

我正在设置一个配置以在 create-react-app + typescript 应用程序(我已经退出)中运行我的测试。我正在使用玩笑+酶。在我的 tsconfig.json 中,我设置了 baseUrl='./src',因此我可以在导入模块时使用绝对路径。例如,这是我的一个文件中的典型导入语句:

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 配置中?

感谢您的意见和建议。

【问题讨论】:

    标签: typescript visual-studio-code jestjs create-react-app absolute-path


    【解决方案1】:

    使用最佳实践的解决方案

    出现此错误是因为在使用 Jest 时在我们的 TypeScript/Nest.js/Angular 项目的 import 语句中使用了绝对路径。使用moduleDirectoriesmoduleNameMapper 选项修复它可能会暂时解决您的问题,但它会在其他包(例如TypeORM issue)中产生问题。另外,Nest.js 框架的创建者suggests 认为使用绝对路径是一种不好的做法。


    绝对路径与相对路径

    import 带有 绝对路径 的语句看起来像:

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

    import 带有 相对路径 的语句看起来像:

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

    VS 代码设置

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

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

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


    修复项目中的导入

    现在在项目文件中,查找与上述示例类似的导入中的绝对路径,然后将其删除。您将看到已删除软件包的错误。只需使用自动导入建议并将它们导入回来。这次将使用相对路径导入它们。这一步可能会很乏味,具体取决于您的项目规模,但从长远来看是值得的。


    希望能成功!干杯!

    【讨论】:

      【解决方案2】:

      对我来说,我只需要添加

      "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"],
      

      从阅读the docs 看来,这是一个附加目录列表,因此node_modules 是不必要的。

      【讨论】:

      • 不确定为什么这个答案被否决了 - 这是一个干净的答案,至少可以与较新版本的 jest 完美配合(我在这里使用 v27+),并且是该问题的推荐解决方案文档本身。唯一的问题是,在大多数情况下,当您的 jest 配置位于项目根目录时,您实际上并不需要 &lt;rootDir&gt; 部分,src 也可以正常工作。
      • 就是这样,谢谢你在过去的一个小时里一直在挠我的头!
      • @xyzen 谢谢./src 也可以,但src 不行
      【解决方案3】:

      在您的 package.json 中添加以下部分。进行更改后,不要忘记重新启动测试观察者。

        "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"
          }
        }
      

      【讨论】:

      • 当我在使用 nestjs 框架时遇到这个问题时为我工作。我对 package.json 进行了编辑,现在可以使用绝对路径运行我的测试。
      • 进行更改后不要忘记重新启动您的测试观察者
      • 如前所述,如果使用 NEST 框架,这将非常有效。
      • 这个也帮助我使用NestJS,谢谢!
      • 这对您来说仍然是一个有效的解决方案吗?
      【解决方案4】:

      使用 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>/' }),
      }
      

      【讨论】:

        【解决方案5】:

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

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

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

        【讨论】:

          【解决方案6】:

          ts-jest 可以完美解决这个问题!
          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>/' } */)
              }
          

          【讨论】:

            【解决方案7】:

            如果您在 monorepo 中遇到这种情况,这就是为我解决问题的方法:

            内部jest.config.js

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

            假设你在tsconfig.json

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

            【讨论】:

              【解决方案8】:

              这对我有用:

              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"],
              };
              

              就我而言,tsconfig.json 中没有任何 paths,但我将 baseUrl 设置为 src

              【讨论】:

                【解决方案9】:

                package.json 中将以下内容添加到我的 jest 配置中为我解决了这个问题。

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

                【讨论】:

                  【解决方案10】:

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

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

                  之前

                  moduleDirectories: ['node_modules']
                  

                  之后

                  moduleDirectories: ['node_modules', 'src']
                  

                  希望对你有帮助。

                  【讨论】:

                  • 我在 package.json 中添加了这个,在 jest 下。
                  • 这可能很明显,但是如果您在导入中包含 src 路径,例如“... from 'src/...'”,请添加:moduleDirectories: ['node_modules', '.']
                  【解决方案11】:

                  正如这里许多人指出的那样,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 中定义的路径。这是一个Gist code snippet

                  // 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)
                  };
                  

                  【讨论】:

                  • 谢谢。这如何处理为别名定义的多个位置(如"/opt/*": ["first/*", "second/*"])?
                  【解决方案12】:

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

                  我的问题:

                  • 将 create-react-app 与 typescript 结合使用
                  • 使用绝对路径 (src/MyComp) 在其他组件(例如 App.tsx)中导入组件
                  • 它正在编译/运行/构建
                  • 没有正在进行测试

                  我发现错误是由于 NODE_PATH 的值不同造成的。所以我将它设置为测试运行。

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

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

                  【讨论】:

                    【解决方案13】:

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

                    我使用了@Antonie Laffargue 提到的配置(添加/编辑属性moduleDirectories: ['node_modules', 'src']),效果很好。

                    【讨论】:

                      【解决方案14】:

                      这就是我如何让 moduleNameMapper 工作。

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

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

                      这是模块名称映射器:

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

                      【讨论】:

                        【解决方案15】:

                        您可能想要 moduleNameMapper 开玩笑配置的功能。 它会将您的自定义导入命名空间映射到真实的模块位置。

                        在此处查看官方文档:

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

                        【讨论】:

                        • 感谢您的回答。我尝试像这样使用 moduleNameMapper: moduleNameMapper: { ".": "./src" } 但现在我的测试挂起并显示以下消息:Determining test suites to run... 当我映射我的路径时,它看起来像使用了 moduleNameMapper .但就我而言,我没有映射任何路径。我刚刚指定了一个 baseUrl
                        • 我遇到了同样的问题。有没有人找到解决方案?我试过“moduleDirectories”:[“src”],但它不起作用。
                        • 这里做了很多尝试,但我找到了moduleNameMapper的解决方案。
                        猜你喜欢
                        • 2018-11-24
                        • 2021-01-09
                        • 1970-01-01
                        • 1970-01-01
                        • 2012-06-29
                        • 1970-01-01
                        • 2021-08-25
                        • 2022-11-07
                        相关资源
                        最近更新 更多