测试代码报错:this.instructionsService.instruction.subscribe不是函数
问题:
this.instructionsService.instruction.subscribe is not a function 错误 测试代码时遇到错误:this.instructionsService.instruction.subscribe is not a function,错误出现在组件ngOnInit方法首行以及测试用例调用component.ngOnInit()处。已模拟InstructionsService并将instruction设为Observable,但问题仍存在。
相关代码文件
1. instruction-context.component.spec.ts
let instructionService: InstructionsService; let mockInstructionService: = { instruction: new BehaviorSubject<any>({}).asObservable(), applyInstruction: () => {}, }; let expectedInstructions = {[]}// object with some expected data beforeEach( waitForAsync(() => { TestBed.configureTestingModule({ declarations: [InstructionContextComponent], imports: [HttpClientTestingModule], providers: [ { provide: InstructionsService, useValue: mockInstructionService}, HttpService ] }).compileComponents(); }) ); beforeEach(() => { fixture = TestBed.createComponent(InstructionContextComponent); instructionService = TestBed.inject(InstructionsService); component = fixture.componentInstance; fixture.detectChanges(); }); describe('ngOninit', () => { let instrcutionServiceSpyApplyInstruction; beforeEach(() => { instrcutionServiceSpyApplyInstruction = spyOn(instructionService, 'applyInstruction'); spyOn(instructionService , 'instruction' as any).and.returnValue(of(expectedInstructions)); }); it('should create', () => { //Assert expect(component).toBeTruthy(); }); it('should apply correct state when mode is "create" and locationData is present', fakeAsync(() => { // Arrange component.mode = 'create'; component['locationData'] = [{ id: '1', name: 'Node 1' }] as any; // Act component.ngOnInit(); tick(100); // Assert expect(clearInterval).toHaveBeenCalled(); expect(instrcutionServiceSpyApplyInstruction ).toHaveBeenCalled(); })); })
2. instructionService.ts
public instruction: BehaviorSubject<Instructions>; set instructions(){ let state //setting some values for state this.instruction.next({ ...state }); }
3. instruction-context.component.ts
ngOninit(){ this.instructionsService.instruction.subscribe((instruction: IInstruction) => { this.instruction = instruction; this.instructionLevel = this.instruction && this.instruction['level']; this.instructionContext = this.instruction && this.instruction['context']; if (this.instructionContext) { this.locationData = this.instructionContext['locationData']; if (this.instructionContext['types'] && this.instructionContext['types'][this.mode]) { this.filterNodes = this.instructionContext['types'][this.mode][ 'filterNodes' ]; this.tree = this.instructionContext['types'][this.mode]['treeData']; this.selectedDevicesCount = Array.isArray( this.instructionContext['types'][this.mode]['selectedNodes'] ) ? this.instructionContext['types'][this.mode]['selectedNodes'].length : 0; } } this.validity.emit(this.checkFormValidity()); });
问题根源及修复方案
1. 核心问题:错误的属性Spy用法
spyOn默认只能用于方法,你尝试对instruction这个属性使用spyOn,会直接覆盖原有的Observable,导致它变成非函数类型,自然无法调用subscribe方法。
2. 修复步骤
步骤1:保留BehaviorSubject实例,方便控制数据
将mock中的Subject实例单独提取,后续通过它发送测试数据,无需spy属性:
// 提升Subject到全局,方便所有测试用例访问 let instructionSubject: BehaviorSubject<any>; let mockInstructionService: Partial<InstructionsService>; beforeEach(waitForAsync(() => { instructionSubject = new BehaviorSubject<any>({}); mockInstructionService = { instruction: instructionSubject.asObservable(), applyInstruction: () => {}, }; TestBed.configureTestingModule({ declarations: [InstructionContextComponent], imports: [HttpClientTestingModule], providers: [ { provide: InstructionsService, useValue: mockInstructionService}, HttpService ] }).compileComponents(); }));
步骤2:移除错误的属性Spy,直接发送测试数据
在测试用例中,通过Subject的next方法发送预期数据,替代spy操作:
describe('ngOnInit', () => { let instrcutionServiceSpyApplyInstruction; beforeEach(() => { instrcutionServiceSpyApplyInstruction = spyOn(instructionService, 'applyInstruction'); // 直接发送测试数据 instructionSubject.next(expectedInstructions); }); // 测试用例... });
步骤3:修复组件生命周期钩子拼写错误
组件中的方法名应为ngOnInit(大写O),你当前写的是ngOninit(小写o),这会导致Angular无法自动触发该钩子,同时手动调用时也可能出现逻辑异常:
// 组件中修正方法名 ngOnInit() { this.instructionsService.instruction.subscribe((instruction: IInstruction) => { // ... 原有逻辑 }); }
步骤4:修正测试数据语法错误
expectedInstructions = {[]} 存在语法错误,改为符合IInstruction类型的合法对象,比如:
let expectedInstructions = { level: 'test-level', context: { locationData: [], types: {} } };
额外优化建议
测试中fixture.detectChanges()会自动触发ngOnInit,如果需要手动调用该方法,建议在beforeEach中移除fixture.detectChanges(),改为在测试用例中按需调用,避免重复执行钩子逻辑。
内容的提问来源于stack exchange,提问作者Learner.123
相关产品推荐
相关产品推荐

