Search code examples
angularjasmineangular-test

Cannot read properties of undefined (reading 'subscribe') in Unit Test NgOnInIt


I tried a lot of SO Question's answers but nothing worked for this.so in mt test im doing something like this.

const stubStudentService = jasmine.createSpyObj('StudentsCrudService',['getAllStudents','updateStudent']);
const stubWebSocketService = jasmine.createSpyObj('WebsocketService',['getNotified']);

describe('StudentsListComponent', () => {
  let component: StudentsListComponent;
  let fixture: ComponentFixture<StudentsListComponent>;;
  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [StudentsListComponent],
      imports: [],
      providers: [
        { provide: StudentsCrudService, useValue: stubStudentService },
        { provide: WebsocketService, useValue: stubWebSocketService },
        NotificationService,
      ],
    }).compileComponents();
  });

  beforeEach(() => {
    fixture = TestBed.createComponent(StudentsListComponent);
    stubStudentService.getAllStudents.and.returnValue(of(studentPayload));
    stubWebSocketService.getNotified.and.returnValue(of('success'));
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

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

In my component, it look like this

  constructor(
    private studentService: StudentsCrudService,
    private notificationService: NotificationService,
    private webSocketNotifier: WebsocketService
  ) {}

  ngOnInit(): void {
    this.studentSubscription();
    this.webSocketSubscription();
  }

  studentSubscription() {
    this.studentQueryRef =  this.studentService
    .getAllStudents()
    this.initSubscription.add(
      this.studentQueryRef
        .valueChanges.subscribe((response) => {
          this.paginatedStudents = {...response.data.paginateStudents}
        })
    );
  }

When I run the test im getting Cannot read properties of undefined (reading 'subscribe') at StudentsListComponent.studentSubscription. I tried so many things but nothing worked.


Solution

  • The first fixture.detectChanges is when ngOnInit is called.

    You have mocked studentService correctly but you haven't mocked the return of getAllStudents incorrectly.

    Try this (follow comments with !!):

    const stubStudentService = jasmine.createSpyObj('StudentsCrudService',['getAllStudents','updateStudent']);
    const stubWebSocketService = jasmine.createSpyObj('WebsocketService',['getNotified']);
    
    describe('StudentsListComponent', () => {
      let component: StudentsListComponent;
      let fixture: ComponentFixture<StudentsListComponent>;
      // !! make the following changes
      let stubStudentService: jasmine.SpyObj<StudentCrudService>;
      let stubWebSocketService: jasmine.SpyObj<WebsocketService>;
      
      beforeEach(async () => {
        // !! assign the spies in a beforeEach so they are fresh for every test
        stubStudentService = jasmine.createSpyObj('StudentsCrudService', 
          ['getAllStudents','updateStudent']);
        stubWebSocketService = jasmine.createSpyObj('WebsocketService', 
        ['getNotified']);
    
        await TestBed.configureTestingModule({
          declarations: [StudentsListComponent],
          imports: [],
          providers: [
            { provide: StudentsCrudService, useValue: stubStudentService },
            { provide: WebsocketService, useValue: stubWebSocketService },
            NotificationService,
          ],
        }).compileComponents();
      });
    
      beforeEach(() => {
        fixture = TestBed.createComponent(StudentsListComponent);
        // !! change below to return object of valueChanges
        stubStudentService.getAllStudents.and.returnValue({ valueChanges: of(studentPayload) });
        stubWebSocketService.getNotified.and.returnValue(of('success'));
        component = fixture.componentInstance;
        // !! the below fixture.detectChanges will call ngOnInit
        fixture.detectChanges();
      });
    
     it('should create', () => {
      expect(component).toBeTruthy();
     });
    

    The above should hopefully fix your problem.