9

I am trying to test a component which has ngOnDestroy() method which has all the unsubscribe() method calls. But, while testing, when I run my testing file (spec file) it gives me error saying:

cannot read property 'unsubscribe' of undefined

I have done the following import in my testing file:

import { Subscription }   from 'rxjs/Subscription';

so I think this should be enough to get all the methods in Subscription, but still the error is there. Also, I tried adding 'Subscription' to my 'imports', 'declarations' and 'providers' list of the testing file, still the error is there.

Below is a code snippet:

//component
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subscription }   from 'rxjs/Subscription';
import {
 NinjaService } from '../../../../ninja.service';


 @Component({
 selector: 'ninja-files',
 templateUrl: './ninja-files.component.html',
 styleUrls: ['./ninja-files.component.css']
})
 export class ninjaFilesComponent implements OnInit, OnDestroy {

 showNinjaFiles: boolean = true;

 addFileSubscription: Subscription;

 constructor(private ninjaService: NinjaService) {
 ........
}

 ngOnInit() {
  this.addFileSubscription = this.NinjaService.AddedFile$
  .subscribe((fileFormat: FileFormat) => {
  console.log('File being added: ' + fileFormat.fileName); }

  ngOnDestroy() {
  this.addFileSubscription.unsubscribe();
}
}

Then I have the testing file for this component as follows:

import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subscription }   from 'rxjs/Subscription';
import {
 NinjaService } from '../../../../ninja.service';
import { TestBed, async, fakeAsync ,ComponentFixture, } from '@angular/core/testing';
import { DebugElement }    from '@angular/core';
import { By }              from '@angular/platform-browser';
import {} from 'jasmine';

describe('Component: NinjaFiles', () => {

  let fixture: ComponentFixture<ninjaFilesComponent>;
  let component: ninjaFilesComponent;
  let ninjaServiceStub;

beforeEach( async(() => {
 //stub NinjaService
 let ninjaServiceStub= {};

 fixture = TestBed.configureTestingModule({
 declarations: [ ninjaFilesComponent],
}).createComponent(ninjaFilesComponent);
 component = fixture.componentInstance;

}));

it('Page Title', async(() => {
 let pageTitle = fixture.debugElement.query(By.css('.page-title'));
 expect(pageTitle).toBeTruthy();
}));

 it('Counting the files', () => {
  let fileCount= fixture.debugElement.query(By.css('.file-count'));
  expect(fileCount).toBeTruthy();
});

When I run the above testing code, it gives me error saying "cannot read property 'unsubscribe' of undefined" (which means its not being able to define the subscription object 'addFileSubscription' that I've defined in the component class.

Can somebody suggest a workaround?

Aiguo
  • 3,416
  • 7
  • 27
  • 52

2 Answers2

12
import { Subscription } from 'rxjs/Subscription';

All this means is that you can access the Subscription class from the ninjaFilesComponent class file.

The problem is that the addFileSubscription is never initialized. In order for the ngOnInit to be called, you need to call

fixture.detectChanges();

Aside from that, the other problems I see are:

  1. You never add the stub to the providers

    TestBed.configureTestingModule({
      providers: [
        { provide: NinjaService, useValue: ninjaServiceStub }
      ]
    })
    
  2. You need to actually implement something in the stub

    let ninjaServiceStub = {
      AddedFile$: Observable.of(new FileFormat(...))
    }
    
  3. You're not using the service in the component correctly

    this.NinjaService.AddedFile$
    // should be
    this.ninjaService.AddedFile$
    // lowercase
    
Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
  • thanks for the suggestion @peeskillet! One more question, the 'FileFormat' here is an interface, so I can't use something like 'AddedFile$: Observable.of(new FileFormat(...))' in my ninjaServiceStub. Do you have a suggestion how to create an observable.of an interface? – Aiguo Oct 29 '16 at 14:56
  • You don't need to use the class. Whatever the structure of the interface is just use an object literal, like `{ whatever: "properties", it: "has" }` – Paul Samsotha Oct 29 '16 at 14:58
  • [read this](http://www.typescriptlang.org/docs/handbook/type-compatibility.html) to get a better understanding about how typescript types work – Paul Samsotha Oct 29 '16 at 15:00
1

Adding fixture.detectChanges(); to my tests resolved this issue which I was facing when running tests using Angular 2 CLI with Material Design.

Thanks for the workaround!

Headcult
  • 183
  • 9