3

Suppose i have a service named testService with a function getData in it. Also i have a component ( say A ) into which the service is injected.

export class A implements OnInit,OnDestroy{
    saveObj;

    constructor(public service:testService){

    }

    ngOnDestroy(){
      if(this.saveObj) this.saveObj.unsubscribe();
    }

    ngOnInit(){
        this.saveObj = this.service.getData.subscribe(res => {
            this.func(res);
        },
        err => {
            console.log("Error Occured");
            this.saveObj.unsubscribe();
        });
    }

    private func(result: any){
        // Some code
    }
}

Now i am doing unit testing for this component. The problem is in some cases , it throws an error:

Uncaught TypeError: _this.saveObj.unsubscribe is not a function thrown

Code snippet of spec.ts:

// testServiceStub is just a mock of testService. 

beforeEach(async(()=>{
    testServiceStub = jasmine.createSpyObj(['getData']);

    TestBed.configureTestingModule({
        declarations : [A],
        schemas : [NO_ERRORS_SCHEMA],
        providers : [
            { provide : testService, useValue: testServiceStub }
        ]
    }).compileComponents();
}))

beforeEach(async(()=>{
    fixture = TestBed.createComponent(A);
    component = fixture.componentInstance;
}))

it('checks whether error is handled or not',()=>{
    spyOn(console,'log');
    testServiceStub.getData.and.returnValue(throwError({status:404})); 
    fixture.detectChanges();
    expect(console.log).toHaveBeenCalled(); // shows the TypeError
})


it('checks whether value is handled or not',()=>{
    testServiceStub.getData.and.returnValue(of(mockData)); // some mock data
    fixture.detectChanges();
    expect(component.func).toHaveBeenCalled();  // also shows the TypeError
})

I also referred this link unsubscribe is not a function on an observable . But the problem is it also works in some cases and no error is shown.

Please help me figure out the reason and possible scenarios.

UPD: Added onDestroy lifecycle hook

12
  • Please do null check before you unsubscribe the subscriptions. And also this can be done within ngOnDestroy lifecycle method Commented May 11, 2020 at 9:14
  • 1
    Hello Amit, i added it still the error remains. Can you tell me why this error comes at all? and also when it would work without fail? Commented May 11, 2020 at 9:20
  • @RahulChowdhury I already gave an answer about Subscriptions and how to properly unsubscribe from observables on destroy. Please try my explained approach with Subject and takeUntil here --> stackoverflow.com/questions/57355066/… Commented May 11, 2020 at 14:26
  • 1
    @RahulChowdhury I have added a small demo. Hope that should clear your doubts. Check the console logs where the first sub is undefined. Commented May 12, 2020 at 6:35
  • 2
    Yes thank you. It cleared what you said. Mind checking the code which i wrote in the answer? is it fine? Commented May 12, 2020 at 7:45

1 Answer 1

2

As suggested by @Amit Chigadani

export class A implements OnInit,OnDestroy{
    saveObj: Subscription ;

    constructor(public service:testService){

    }

    ngOnDestroy(){
      if(this.saveObj) 
             this.saveObj.unsubscribe();
    }

    ngOnInit(){
        this.saveObj = this.service.getData.subscribe(res => {
            this.func(res);
        },
        err => {
            console.log("Error Occured");
        });
    }

    private func(result: any){
        // Some code
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

This is pretty nasty. This field is declared as a Subscription, not a Subscription | undefined, yet it is not initialized in the constructor, so it must be undefined for some period of time after this object is created. We should be able to catch these errors at compile time, but for some reason the TypeScript compiler doesn't catch this even with strict mode turned on.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.