Angular 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

