4
votes

I got private class members in my component, for example

private querySubscription: Subscription;.

In my ngOnDestroy() I unsubscribe from my subscription:

ngOnDestroy(): void { this.querySubscription.unsubscribe(); this.userSettingsSubscription.unsubscribe(); }

How do I test that I have really unsubscribed correctly in my unit test in jasmine? I can programmatically call the ngOnDestory() because it is public, but I can not test my private class members.

3
I believe that you need to somehow mock/spy upon place where you assign observable(or other) subscription to querySubscription and userSettingsSubscription and then replace(with spy?) unsubscribe method to know whenever it was called or not - but it's only an idea.Buczkowski

3 Answers

4
votes

You should mock your private variable (which you can access through array notation), then call your function, and expect the subscrpition to be closed.

Stackblitz

  it('should unsubscribe on destroy', () => {
    component['querySubscription'] = of(true).subscribe();

    component.ngOnDestroy();

    expect(component['querySubscription'].closed).toBeTruthy();
  });

EDIT 1

This solution works because of array notation. In javascript, you don't have private or public variables : you only have variables.

Typescript forces you to comply with private/public, but you can always work around that.

If you wish not to do that, you should create a public function that creates a subscription like so

createSubscription() {
  this.querySubscription = this.someFunction().subscribe();
}

This is the only other viable solution if you don't want to use the array notation (which, I forgot to mention, is not a bad practice at all).

Up to you to choose which one you prefer !

0
votes

May be something like:

export class ObservableHelper {
    getMockObservable() {
        const s = new Subscription();
        const obs = new Observable();
        spyOn(obs, 'subscribe').and.returnValue(s);
        spyOn(s, 'unsubscribe');
        return {obs, s};
    }
}

in your spec file:

let helper = new ObservableHelper();
let o1 = helper.getMockObservable();

One of the comment it is already mentioned that Mock has to be done when a new subscription is created. If you are calling an external service and that returns an observable, o1.obs can be used as return value for same.

e.g. if you are subscribing for a PubSub service:

pubSubSpy.sub.withArgs(jasmine.any(Object), 'test').and.returnValue(o1.obs);


// Act
service.ngOnDestroy();

// Assert
expect(o1.s.unsubscribe).toHaveBeenCalledTimes(1);
-1
votes

If you're using Typescript and I suppose you are to test private members in your test module when setting up testBed you have to call your service using injector.

beforeEach(() => {
    TestBed.configureTestingModule({
        providers: [
            YourService,
        ],
        imports: [ HttpClientTestingModule ],
    });
    const injector = getTestBed();
    service = injector.get(YourService);
}); 

Now you can access your service public methods by using service.myMethod and your private ones by using (service as any).privateMethod().

As typescript is always compiled to JavaScript and in JavaScript you do not have public private members you have to use the (service as any) construct.

EDIT 1

After you are able to access a private, you can access it's properties, so what you then do is (in pseudocode):

    spyOn((service as any).privateMethod, 'setBar');

    service.ngOnDestroy()

    expect((service as any).privateMethod.setBar).toHaveBeenCalled();