单元测试因找不到模块而失败,但VS Code能找到该路径

前端开发 2026-07-09

我正在把Angular从 20升级到21。

我的所有单元测试都失败了,混合出现了“Cannot find module” @angular/core/testing, @angular/common/http/testing@angular/common/http/testing

src/app/service/user-group-service.spec.ts:1:25 - error TS2307: Cannot find module '@angular/core/testing' or its corresponding type declarations.

1 import { TestBed } from '@angular/core/testing';
                        ~~~~~~~~~~~~~~~~~~~~~~~
src/app/service/user-group-service.spec.ts:3:65 - error TS2307: Cannot find module '@angular/common/http/testing' or its corresponding type declarations.

3 import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
                                                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/app/service/user-group-service.spec.ts:4:35 - error TS2307: Cannot find module '@angular/common/http' or its corresponding type declarations.

4 import { provideHttpClient } from '@angular/common/http';
                                ~~~~~~~~~~~~~~~~~~~~~~

我在VS Code里可以输入 import TestBed,并得到自动完成提示,所以我知道这些文件确实存在。

但在执行 npm run test 时(它被简单映射到 jest 命令)单元测试失败。

这就说明Jest或 TypeScript出现了问题。

我对配置文件做的任何修改似乎都无法解决这个问题。

$ ng version
Angular CLI       : 21.2.12
Angular           : 21.2.14
Node.js           : 22.22.2
Package Manager   : npm 11.12.0
Operating System  : darwin arm64

┌───────────────────────────────────┬───────────────────┬───────────────────┐
│ Package                           │ Installed Version │ Requested Version │
├───────────────────────────────────┼───────────────────┼───────────────────┤
│ @angular-devkit/build-angular     │ 21.2.12           │ ~21.2.12          │
│ @angular/animations               │ 21.2.14           │ ~21.2.14          │
│ @angular/cdk                      │ 20.2.14           │ ^20.2.14          │
│ @angular/cli                      │ 21.2.12           │ ~21.2.12          │
│ @angular/common                   │ 21.2.14           │ ~21.2.14          │
│ @angular/compiler                 │ 21.2.14           │ ~21.2.14          │
│ @angular/compiler-cli             │ 21.2.14           │ ~21.2.14          │
│ @angular/core                     │ 21.2.14           │ ~21.2.14          │
│ @angular/forms                    │ 21.2.14           │ ~21.2.14          │
│ @angular/language-service         │ 21.2.14           │ ~21.2.14          │
│ @angular/platform-browser         │ 21.2.14           │ ~21.2.14          │
│ @angular/platform-browser-dynamic │ 21.2.14           │ ~21.2.14          │
│ @angular/router                   │ 21.2.14           │ ~21.2.14          │
│ rxjs                              │ 7.8.2             │ ~7.8.2            │
│ typescript                        │ 5.9.3             │ ^5.9.3            │
│ zone.js                           │ 0.15.1            │ ~0.15.1           │
└───────────────────────────────────┴───────────────────┴───────────────────┘
// tsconfig.json
{
  "compileOnSave": false,
  "isolatedModules": true,
  "compilerOptions": {
    "baseUrl": "./",
    "outDir": "./dist/out-tsc",
    "esModuleInterop": true,
    "sourceMap": true,
    "declaration": false,
    "module": "es2015",
    "moduleResolution": "bundler",
    "experimentalDecorators": true,
    "importHelpers": false,
    "target": "es2022",
    "typeRoots": [
      "node_modules/@types"
    ],
    "useDefineForClassFields": false
  }
}
// jest.config.ts
import type { Config } from 'jest';

const config: Config = {
    verbose: true,
    preset: 'jest-preset-angular',
    injectGlobals: true,
    testEnvironment: 'jest-environment-jsdom',
    transform: {
    '^.+\\.(ts|mjs|js|html)$': [
        'jest-preset-angular',
        {
            tsconfig: '<rootDir>/tsconfig.spec.json',
            stringifyContentPathRegex: '\\.(html|svg)$',
            useESM: true,
        },
    ],
    },
    reporters: ['jest-simple-dot-reporter'],
    setupFilesAfterEnv: ['<rootDir>/setup-jest.ts'],
    modulePathIgnorePatterns: [
    // These ignores are needed when run inside the container
    "<rootDir>/.SAVED",
    "<rootDir>/.container_node_modules_amd64"
    ],
    moduleNameMapper: {
        '^src/(.*)$': '<rootDir>/src/$1',
        '^app/(.*)$': '<rootDir>/src/app/$1',
        '^assets/(.*)$': '<rootDir>/src/assets/$1',
        '^environments/(.*)$': '<rootDir>/src/environments/$1',
    },
    collectCoverage: true,
    collectCoverageFrom: [
        // '**/*.{ts,js,jsx}',
        'src/**/*.{ts,js,jsx}', '!src/environments/**'
    ],
    coverageProvider: 'v8',
    coverageReporters: [
        'html',
        'text-summary',
    ],
    coveragePathIgnorePatterns: [
        'src/app/model',
        'src/app/mailing-lists/model',
        'src/main.ts',
        'src/polyfills.ts',
        'src/zone-flags.ts',
        'src/app/app.module.ts',
    ]
};

export default config;
// tsconfig.spec.json
{
  "compileOnSave": false,
  "extends": "./tsconfig.json",
  "include": ["src/**/*.spec.ts", "src/**/*.d.ts"],
  "compilerOptions": {
    "baseUrl": "./",
    "outDir": "./dist/out-tsc",
    "esModuleInterop": true,
    "sourceMap": true,
    "declaration": false,
    "module": "es2015",
    "moduleResolution": "node",
    "experimentalDecorators": true,
    "importHelpers": true,
    "target": "ESNext",
    "types": ["jest", "node"],
    "typeRoots": [
      "node_modules/@types"
    ],
    "lib": [
      "es2018",
      "dom"
    ],
    "useDefineForClassFields": false
  }
}

解决方案

可以尝试这里的解决方案,针对失败的测试有类似的问题: https://github.com/nrwl/nx/issues/33777#issuecomment-3674675041

我们通过在tsconfig.spec.json中添加以下内容使其工作:

"allowSyntheticDefaultImports": true, "resolveJsonModule": true, "isolatedModules": true

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章