Angular中从共享库导入Service时的Jest单元测试问题
自定义Angular库导入后Jest测试报错解决方案
问题场景
在OrdersComponent中导入自定义Angular库my-lib/ui的CommonService后,运行Jest单元测试时出现以下错误:
● Test suite failed to run TypeError: Cannot read properties of undefined (reading 'toLowerCase') > 16 | import { CommonService } from 'my-lib/ui'; | ^ at registerLocaleData (node_modules/@angular/core/fesm2022/core.mjs:20319:25) at registerLocaleData (node_modules/@angular/common/fesm2022/common.mjs:2561:12) at Object.<anonymous> (node_modules/my-lib/fesm2022/my-lib-ui.mjs:291:1) at Object.<anonymous> (src/app/pages/orders/orders.component.ts:16:1) at Object.<anonymous> (src/app/pages/orders/orders.component.spec.ts:3:1)
相关代码如下:
OrdersComponent导入片段
... import { CommonService } from 'my-lib/ui'; @Component({ selector: 'app-orders', templateUrl: './orders.component.html', styleUrls: ['./orders.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush }) export class OrdersComponent implements OnInit, OnDestroy {
Jest测试代码
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { MatDialogModule } from '@angular/material/dialog'; import { MatSnackBarModule } from '@angular/material/snack-bar'; import { OrdersComponent } from './orders.component'; describe('OrdersComponent', () => { let component: OrdersComponent; let fixture: ComponentFixture<OrdersComponent>; beforeEach(() => { TestBed.configureTestingModule({ imports: [RouterTestingModule, HttpClientTestingModule, MatDialogModule, MatSnackBarModule], declarations: [OrdersComponent] }); fixture = TestBed.createComponent(OrdersComponent); component = fixture.componentInstance; fixture.detectChanges(); }); it('Orders should create', () => { expect(component).toBeTruthy(); }); });
解决方案
1. Mock自定义服务(优先方案)
测试组件时无需依赖真实的库服务实现,直接MockCommonService即可避免库内部的依赖问题,有两种实现方式:
方式一:TestBed提供Mock类
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { CommonService } from 'my-lib/ui'; import { RouterTestingModule } from '@angular/router/testing'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { MatDialogModule } from '@angular/material/dialog'; import { MatSnackBarModule } from '@angular/material/snack-bar'; import { OrdersComponent } from './orders.component'; // 模拟服务类,按需实现组件用到的方法/属性 class MockCommonService { // getOrderList = jest.fn().mockReturnValue([]); } describe('OrdersComponent', () => { let component: OrdersComponent; let fixture: ComponentFixture<OrdersComponent>; beforeEach(() => { TestBed.configureTestingModule({ imports: [RouterTestingModule, HttpClientTestingModule, MatDialogModule, MatSnackBarModule], declarations: [OrdersComponent], providers: [ { provide: CommonService, useClass: MockCommonService } ] }); fixture = TestBed.createComponent(OrdersComponent); component = fixture.componentInstance; fixture.detectChanges(); }); it('Orders should create', () => { expect(component).toBeTruthy(); }); });
方式二:jest.mock直接Mock整个库
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { MatDialogModule } from '@angular/material/dialog'; import { MatSnackBarModule } from '@angular/material/snack-bar'; import { OrdersComponent } from './orders.component'; // Mock库的导出,模拟服务实例 jest.mock('my-lib/ui', () => ({ CommonService: jest.fn().mockImplementation(() => ({ // 按需模拟组件调用的方法 // fetchData: jest.fn().mockResolvedValue({}) })) })); describe('OrdersComponent', () => { let component: OrdersComponent; let fixture: ComponentFixture<OrdersComponent>; beforeEach(() => { TestBed.configureTestingModule({ imports: [RouterTestingModule, HttpClientTestingModule, MatDialogModule, MatSnackBarModule], declarations: [OrdersComponent] }); fixture = TestBed.createComponent(OrdersComponent); component = fixture.componentInstance; fixture.detectChanges(); }); it('Orders should create', () => { expect(component).toBeTruthy(); }); });
2. 排查库内部的Locale配置问题
报错栈显示错误源于registerLocaleData调用时传入了undefined参数,导致toLowerCase调用失败。检查my-lib/ui库的代码,确保正确导入并传递locale数据:
错误示例(会触发问题):
import { registerLocaleData } from '@angular/common'; // localeData未正确导入,值为undefined registerLocaleData(localeData);
正确示例:
import { registerLocaleData } from '@angular/common'; // 导入具体的locale数据文件 import localeZh from '@angular/common/locales/zh'; registerLocaleData(localeZh);
3. Jest额外配置(可选)
如果Mock后仍存在模块解析问题,可在jest.config.js中配置moduleNameMapper,确保Jest能正确解析库的路径:
module.exports = { // 其他Jest配置项... moduleNameMapper: { '^my-lib/ui$': '<rootDir>/node_modules/my-lib/fesm2022/my-lib-ui.mjs' } };
内容的提问来源于stack exchange,提问作者Smaillns
相关产品推荐
相关产品推荐

