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

测试代码报错: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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 05:16:35