GeForce RTX 4090
GeForce RTX 4090

Reputation: 3498

How to test multiple sequential calls with Jasmine

I am trying to test a function that unsubscribes from all subscriptions:

ngOnDestroy() {
        this.tryUnsubscribe(this.carsSubscription);
        this.tryUnsubscribe(this.partsSubscription);
        this.tryUnsubscribe(this.shopsSubscription);
    }

This is the test I wrote for the function:

it('should unsubscribe from subscriptions ', () => { 
      spyOn(component, "tryUnsubscribe");     
      component.ngOnDestroy();
      expect(component.tryUnsubscribe).toHaveBeenCalledWith(component['carsSubscription']);
      expect(component.tryUnsubscribe).toHaveBeenCalledWith(component['partsSubscription']);
      expect(component.tryUnsubscribe).toHaveBeenCalledWith(component['shopsSubscription']);
    });

The problem:

If I comment out a function call, the tests still passes.

ngOnDestroy() {
        this.tryUnsubscribe(this.carsSubscription);
        //this.tryUnsubscribe(this.partsSubscription);
        this.tryUnsubscribe(this.shopsSubscription);
    }

Only if I comment out all of these function calls, the test fails:

ngOnDestroy() {
        //this.tryUnsubscribe(this.carsSubscription);
        //this.tryUnsubscribe(this.partsSubscription);
        //this.tryUnsubscribe(this.shopsSubscription);
    }

How to properly test this kind of function? What am I doing wrong?

Upvotes: 12

Views: 17330

Answers (2)

Gennadii Saltyshchak
Gennadii Saltyshchak

Reputation: 637

It is also possible to check all arguments at once using Jasmine calls.allArgs() method:

expect(spy.calls.allArgs()).toEqual([
  [component.carsSubscription],
  [component.partsSubscription],
  [component.shopsSubscription]
]);

Here is modified Fabian Küng's answer in stackblitz.

Upvotes: 6

Fabian Küng
Fabian Küng

Reputation: 6183

I would rewrite your test to the following:

it('should unsubscribe from subscriptions ', () => { 
  const spy = spyOn(component, 'tryUnsubscribe');     
  component.ngOnDestroy();

  // Check how many times the spy was called
  expect(spy).toHaveBeenCalledTimes(3);
});

If you now uncomment one of the tryUnsubscribe calls, the test should fail as the spy was only called twice.

Another approach would be to mock the subscriptions or just set them to a dummy value to test that inside the ngDestroy tryUnsubscribe was called with those 3 component variables:

it('test unsubscribing', () => {
  // Mock values
  component.carsSubscription = Observable.of(1).subscribe(() => {});
  component.partsSubscription = Observable.of(1).subscribe(() => {});
  component.shopsSubscription = Observable.of(1).subscribe(() => {});

  const spy = spyOn(component, 'tryUnsubscribe').and.callThrough();     
  component.ngOnDestroy();

  // Check how many times the spy was called
  expect(spy).toHaveBeenCalledTimes(3);

  // Check arguments
  expect(spy.calls.all()[0].args[0]).toEqual(component.carsSubscription);
  expect(spy.calls.all()[1].args[0]).toEqual(component.partsSubscription);
  expect(spy.calls.all()[2].args[0]).toEqual(component.shopsSubscription);
});

Here is a working stackblitz with the test.

Upvotes: 26

Related Questions