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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 13:50:26