Angular单元测试配置ApiService提供者仍报错的问题排查
问题描述
组件代码(TS)
export class CashFlowSalariesComponent implements OnInit, OnChanges { ... constructor( public apiService: ApiService, private modalService: NgbModal, private toastr: ToastrService ) {} ngOnInit() { } }
单元测试代码(.spec.ts)
describe('CashFlowSalariesComponent', () => { let component: CashFlowSalariesComponent; let fixture: ComponentFixture<CashFlowSalariesComponent>; let apiService: ApiService; beforeEach(async(() => { TestBed.configureTestingModule({ schemas: [ CUSTOM_ELEMENTS_SCHEMA ], imports: [ [RouterModule.forRoot([])], RouterTestingModule, FormsModule, ReactiveFormsModule, BrowserModule, HttpClientTestingModule, ], declarations: [ CashFlowSalariesComponent, ], providers: [{provide: apiService}] }) .compileComponents(); })); beforeEach(() => { fixture = TestBed.createComponent(CashFlowSalariesComponent); component = fixture.componentInstance; fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); });
测试报错信息
NullInjectorError:
StaticInjectorError(DynamicTestModule)[CashFlowSalariesComponent ->
ApiService]: StaticInjectorError(Platform:
core)[CashFlowSalariesComponent -> ApiService]:
NullInjectorError: No provider for ApiService!
提问:我已尝试将ApiService设置为提供者,但并未生效,请问哪里操作错误?
问题原因与修复方案
1. ApiService 提供者配置错误
你在providers中写的{provide: apiService}是核心错误:这里的provide字段需要传入服务类本身(即ApiService),而非你声明的局部变量apiService。
两种修正方式:
- 若要使用真实服务(不推荐单元测试用真实服务,避免依赖外部接口):
providers: [ApiService] - 单元测试更推荐用模拟服务,隔离外部依赖:
// 定义模拟服务,实现组件用到的方法 class MockApiService { // 示例:模拟组件调用的接口方法 getSalaries() { return of([]); } } // 在providers中替换真实服务 providers: [{provide: ApiService, useClass: MockApiService}]
2. 处理其他未配置的依赖
组件构造函数中还有NgbModal和ToastrService,当前测试未为它们配置提供者,修复ApiService后仍会抛出类似注入错误,需一并处理:
class MockNgbModal {} class MockToastrService { success() {} error() {} } // 在TestBed的providers中添加 providers: [ {provide: ApiService, useClass: MockApiService}, {provide: NgbModal, useClass: MockNgbModal}, {provide: ToastrService, useClass: MockToastrService} ]
3. 额外优化点
imports数组中的嵌套数组[RouterModule.forRoot([])]是错误写法,直接移除或改为平铺;且RouterTestingModule已包含路由测试所需依赖,无需重复引入RouterModule。- 单元测试无需引入
BrowserModule,HttpClientTestingModule已覆盖http测试依赖。
修正后的完整测试代码示例
describe('CashFlowSalariesComponent', () => { let component: CashFlowSalariesComponent; let fixture: ComponentFixture<CashFlowSalariesComponent>; let apiService: jasmine.SpyObj<ApiService>; let modalService: jasmine.SpyObj<NgbModal>; let toastrService: jasmine.SpyObj<ToastrService>; // 创建带Spy的模拟服务,方便测试方法调用 beforeEach(async(() => { const apiSpy = jasmine.createSpyObj('ApiService', ['getSalaries']); // 替换为组件实际调用的方法 const modalSpy = jasmine.createSpyObj('NgbModal', ['open']); const toastrSpy = jasmine.createSpyObj('ToastrService', ['success', 'error']); TestBed.configureTestingModule({ schemas: [CUSTOM_ELEMENTS_SCHEMA], imports: [ RouterTestingModule, FormsModule, ReactiveFormsModule, HttpClientTestingModule ], declarations: [CashFlowSalariesComponent], providers: [ {provide: ApiService, useValue: apiSpy}, {provide: NgbModal, useValue: modalSpy}, {provide: ToastrService, useValue: toastrSpy} ] }).compileComponents(); })); beforeEach(() => { fixture = TestBed.createComponent(CashFlowSalariesComponent); component = fixture.componentInstance; // 获取注入的模拟服务实例 apiService = TestBed.inject(ApiService) as jasmine.SpyObj<ApiService>; modalService = TestBed.inject(NgbModal) as jasmine.SpyObj<NgbModal>; toastrService = TestBed.inject(ToastrService) as jasmine.SpyObj<ToastrService>; fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); }); });
内容的提问来源于stack exchange,提问作者Jesus
相关产品推荐
相关产品推荐

