You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 00:20:37