使用Jest测试Angular共享库时遇组件引入错误问题
Angular Jest测试引入项目内UI组件时出现LocaleData未定义错误
项目结构
projects/ |..... app/ |..... src/ |..... app/ |..... app.component.spec.ts |..... app.component.ts |..... app.module.ts |..... lib/ |..... sso/ |...... index.ts |...... ng-package.json |...... ... |...... sso.service.ts |...... ss.module.ts |..... ui/ |...... menu/ |...... menu.component.ts |...... menu.service.ts |...... menu.module.ts |...... index.ts |...... ng-package.json |..... ng-package.json |..... package.json |..... jest.config.ts |..... package.json
AppComponent代码
// app.component.ts import {SSOService} from "my-lib/sso"; @Component({ selector: 'app-root', template: ` <my-menu> <router-outlet></router-outlet> </my-menu> `, styleUrls: ['./app.component.scss'], }) export class AppComponent { constructor(protected ssoService: SSOService) { } }
当前测试代码
// app.component.spec.ts import { TestBed } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; import { AppComponent } from './app.component'; import {SSOService} from "my-lib/sso"; import {MenuComponent} from "my-lib/ui"; describe('AppComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ imports: [ RouterTestingModule ], declarations: [ AppComponent, MenuComponent ], providers: [ { provide: SSOService, useValue: { setRoles: jest.fn() } } ] }).compileComponents(); }); it('should create the app', () => { const fixture = TestBed.createComponent(AppComponent); const app = fixture.componentInstance; expect(app).toBeTruthy(); }); });
错误信息
● Test suite failed to run TypeError: Cannot read properties of undefined (reading 'toLowerCase') 289 | }] }); 290 | > 291 | registerLocaleData(FR); | ^ 292 | /** 293 | * Custom date adapter for Angular Material. 294 | * at registerLocaleData (node_modules/@angular/core/fesm2022/core.mjs:22410:25) at registerLocaleData (node_modules/@angular/common/fesm2022/common.mjs:2562:12) at Object.<anonymous> (dist/lib/fesm2022/my-lib-ui.mjs:291:1) at Object.<anonymous> (projects/app/src/app/app.component.spec.ts:5:1)
我不想MockMenuComponent,之前用Karma测试完全正常,换Jest后出现这个问题,请问是库本身的问题还是Jest配置有误?
相关配置文件
lib/ui/index.ts
// lib/ui/index.ts import { CommonMenuService } from './menu/menu.service'; export * from './menu/menu.component'; export * from './menu/menu.module'; export * from './menu/menu.service';
lib/ui/ng-package.json
{ "lib": { "entryFile": "index.ts" } }
lib/ui/package.json
{ "name": "my-lib", "version": "2.5.2", "peerDependencies": { "@angular/cdk": "^17.0.0", "@angular/common": "^17.0.1", "@angular/core": "^17.0.1", "@angular/material": "^17.0.0", "@awesome-cordova-plugins/in-app-browser": "^6.3.0", "@capacitor/device": "^5.0.6", "angular-oauth2-oidc": "^15.0.1", "jwt-decode": "^4.0.0" }, "dependencies": { "tslib": "^2.6.2" }, "sideEffects": false, "exports": { ".": { "sass": "./styles/_index.scss" } } }
jest.config.ts
// jest.config.ts import type { Config } from 'jest'; const config: Config = { clearMocks: true, collectCoverage: true, coverageDirectory: 'coverage', coverageProvider: 'v8', preset: 'jest-preset-angular', setupFilesAfterEnv: ['<rootDir>/setup-jest.ts'], }; export default config;
tsconfig.json
{ "compileOnSave": false, "compilerOptions": { "baseUrl": "./", "outDir": "./dist/out-tsc", "forceConsistentCasingInFileNames": true, "strict": true, "noImplicitOverride": true, "noPropertyAccessFromIndexSignature": true, "noImplicitReturns": true, "paths": { "my-lib/*": ["projects/lib/*"], "my-lib": ["projects/lib"] }, "noFallthroughCasesInSwitch": true, "sourceMap": true, "declaration": false, "downlevelIteration": true, "experimentalDecorators": true, "moduleResolution": "node", "importHelpers": true, "target": "ES2022", "module": "ES2022", "useDefineForClassFields": false, "lib": ["ES2022", "dom"] }, "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false, "strictInjectionParameters": true, "strictInputAccessModifiers": true, "strictTemplates": true } }
问题分析与解决方案
这个错误的核心原因是Jest加载UI库时,registerLocaleData(FR)中的FR变量未被正确初始化或导入,导致调用toLowerCase时出现undefined错误。Karma能正常运行是因为其构建流程与Jest不同,Karma依赖Angular CLI的构建管道,而Jest直接处理ES模块时可能遗漏Locale相关的初始化逻辑。
解决方案步骤:
检查Locale导入正确性
确认UI库中注册Locale的代码是正确导入的,比如:import { registerLocaleData } from '@angular/common'; import fr from '@angular/common/locales/fr'; registerLocaleData(fr);避免直接使用未导入的
FR变量。在Jest测试初始化中注册Locale
在setup-jest.ts文件中添加Locale注册逻辑,确保测试环境中存在所需的Locale数据:import { registerLocaleData } from '@angular/common'; import fr from '@angular/common/locales/fr'; registerLocaleData(fr);调整Jest路径映射,直接加载源码
修改jest.config.ts,添加moduleNameMapper配置,让Jest直接加载库的源码而非编译后的dist文件:const config: Config = { // 原有配置 moduleNameMapper: { 'my-lib/(.*)': '<rootDir>/projects/lib/$1' } };这样可以避免编译后的库文件出现的初始化问题。
将Locale注册移到NgModule中
不要在全局文件中直接执行registerLocaleData,而是将其移到MenuModule的构造函数或初始化逻辑中:@NgModule({ imports: [CommonModule], declarations: [MenuComponent], exports: [MenuComponent] }) export class MenuModule { constructor() { registerLocaleData(fr); } }这样Locale注册会在模块加载时执行,更符合Angular的生命周期。
内容的提问来源于stack exchange,提问作者Smaillns
相关产品推荐
相关产品推荐

