Jasmine unit testing angular component that subscribes to a service subject in the constructor gives cannot read property subscribe of undefined

I am trying to write a test for the component below.

import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';

import { LoginService } from '../../services/login.service';

@Component({
  selector: 'app-banner',
  templateUrl: './banner.component.html',
  styleUrls: ['./banner.component.css']
})
export class BannerComponent implements OnInit {

  isLoggedIn = false;
  user: User = new User('');

  constructor(
    private loginService: LoginService,
    private router: Router
  ) {
    this.loginService.userWatcher.subscribe(res => {
      this.isLoggedIn = true;
      this.user.oun = res;
    });
  }
...

}

However when I write my test I get this error

TypeError: Cannot read property 'subscribe' of undefined

Here is the spec file:

import { LoginServiceSpy } from '../../services/mock/login.service.spy.spec';
describe('BannerComponent', () => {
  let component: BannerComponent;
  let fixture: ComponentFixture<BannerComponent>;
  const loginService = LoginServiceSpy;
  const user = 'testuser';

  beforeEach(async(() => {

    TestBed.configureTestingModule({
      declarations: [
        BannerComponent,
        MockComponent(SpinnerComponent)
      ],
      imports: [
        FormsModule,
        NgbModule.forRoot(),
        RouterTestingModule
      ],
      providers: [
        { provide: LoginService, useValue: loginService }
      ]
    }).compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(BannerComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  describe('Unit Tests', () => {

    beforeEach(() => {
      localStorage.setItem('currentUser', user);
      loginService.callNextUserWatcher.and.returnValue(asyncData('test'));
    });
...

the following is my login service spy file:

import { createSpyFromClass, Spy } from 'jasmine-auto-spies';
import { LoginService } from '../login.service';

export const LoginServiceSpy: Spy<LoginService> = createSpyFromClass(LoginService);

I have attempted to try the following in my before each in banner.component.spec.ts:

loginService.userWatcher.and.returnValue(Observable.of('test'));

with which I get the response from jasmine: cannot read property and of undefined

any help would be much appreciated.

1 answer

  • answered 2018-07-19 17:58 Amit Chigadani

    This is probably because fixture.detectChanges() on the outer beforeEach() runs first and will load your component. At that time your spy was not yet created and hence you are getting that error.

    Move your spy to outer beforeEach()

    beforeEach(() => {
        fixture = TestBed.createComponent(BannerComponent);
        component = fixture.componentInstance;
        loginService.callNextUserWatcher.and.returnValue(asyncData('test')); // spy created before detect change
        fixture.detectChanges();
      });
    

    Or do not run fixture.detectChanges(); from outer beforeEach()

     beforeEach(() => {
        fixture = TestBed.createComponent(BannerComponent);
        component = fixture.componentInstance;
       // fixture.detectChanges();  -- remove this line
      });
    

    First detectChange should happen after all your app data is initialized.