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

Angular Mock SignalStore测试时为何出现HttpClient空指针错误?

问题:Mock SignalStore后仍出现HttpClient注入错误

我正在测试一个使用SignalStore的组件,相关代码如下:

SignalStore代码

export const TemplateStore = signalStore(
  { providedIn: 'root' },
  withState(initialState),
  withMethods(
    (
      store,
      templateService = inject(TemplatesService)
    )

组件代码

@Component({
  selector: 'app-template-list-layout',
  standalone: true,
  providers: [TemplateStore],
  templateUrl: './template-list-layout.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class TemplateListLayoutComponent {
  templateStore = inject(TemplateStore);

测试代码

beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [TemplateListLayoutComponent],
      providers: [
        {
          provide: TemplateStore,
          useValue: {
            filterTemplates: () => {},
            sortTemplates: () => {},
          },
        },
      ],
    }).compileComponents();

    fixture = TestBed.createComponent(TemplateListLayoutComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });

运行测试时出现如下错误:

NullInjectorError: R3InjectorError(Standalone[TemplateListLayoutComponent])[TemplatesService -> TemplatesService -> HttpClient -> HttpClient]:
NullInjectorError: No provider for HttpClient!

我已Mock了SignalStore,为何仍出现该错误?尝试用useClass替代useValue,结果一致。


解决方案

问题核心是组件自身的providers数组中声明了TemplateStore,这会直接覆盖你在测试模块中配置的Mock版本。Angular初始化组件时,会优先使用组件内部providers里的原始TemplateStore,而非测试模块提供的Mock。

原始TemplateStore的withMethods逻辑中通过inject(TemplatesService)依赖了TemplatesService,而TemplatesService又依赖HttpClient,测试环境未配置HttpClient的Mock,因此抛出注入错误。

可以通过两种方式解决:

方案1:移除组件内的providers声明

你的TemplateStore已经配置了{ providedIn: 'root' },属于根级注入,组件无需再在自身providers中重复声明,直接注入即可。这样测试模块的Mock就能正常生效:

@Component({
  selector: 'app-template-list-layout',
  standalone: true,
  // 移除该行:providers: [TemplateStore],
  templateUrl: './template-list-layout.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class TemplateListLayoutComponent {
  templateStore = inject(TemplateStore);

方案2:在测试中覆盖组件的providers

如果必须保留组件内的providers声明,可以通过overrideComponent方法在测试时替换组件的依赖配置:

beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [TemplateListLayoutComponent],
    })
    // 覆盖组件自身的providers,强制使用Mock的TemplateStore
    .overrideComponent(TemplateListLayoutComponent, {
      set: {
        providers: [
          {
            provide: TemplateStore,
            useValue: {
              filterTemplates: () => {},
              sortTemplates: () => {},
            },
          },
        ],
      },
    })
    .compileComponents();

    fixture = TestBed.createComponent(TemplateListLayoutComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });

两种方案都能确保组件使用Mock版本的TemplateStore,避免触发原始Store的依赖注入逻辑,从而解决HttpClient的错误。


内容的提问来源于stack exchange,提问作者dmance

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 06:07:40