angularunit-testingkarma-jasminerxjs-observablesangular-test

Unable to test my code because of the following error: this.instructionsService.instruction.subscribe is not a function , please see the code below:


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();
   }));
 })

instructionService.ts

public instruction: BehaviorSubject<Instructions>;

set instructions(){
  let state
  //setting some values for state
  this.instruction.next({
      ...state
  });
}

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());
    });

The line I get the error is at this.instructionsService.instruction.subscribe, the first line in my ngOninit() method, in my component and component.ngOnInit() in the spec file. As can be seen in the code 'instruction' is an observable. As per my knowledge ,I have mocked the required dependencies correctly. Not sure what am I missing here. Any help would be highly appreciated!


Solution

  • Try getting rid of the second line in the beforeEach to see if it works. We can only use spyOn for methods/functions, not instance variables like instruction.

    The rest looks ok to me.

    beforeEach(() => {
          instrcutionServiceSpyApplyInstruction = spyOn(instructionService, 'applyInstruction');
          // !! Get rid of this line
          spyOn(instructionService , 'instruction' as any).and.returnValue(of(expectedInstructions));
        });