Search code examples
angulartestingjasminekarma-jasmineangular-test

Angular testing switchMap call called 0 times


In my Angular component I have this code:

  delete(post: PostInterface): void {
    const delete$ = this.appDataService.proxy
      .delete(post, this.paginate)
      .pipe(switchMap(() => this.loadDatas()));

    this.subscriptions.add(
      this.appDataService.start(delete$, 'delete').subscribe()
    );
  }

And I have this test:

beforeEach(async () => {
  loadDatasSpy = spyOn(component, 'loadDatas').and.returnValue(of(paginate));
});

it('should call proxy.delete with model on call delete', fakeAsync(() => {
  loadDatasSpy.calls.reset();

  const id = 1;
  const post: PostInterface = new Post().init({id});

  const proxySpy = jasmine.createSpyObj('ProxyService', ['delete']);
  proxySpy.delete.and.returnValue(of(id));

  const appDataServiceSpy = jasmine.createSpyObj('AppDataService', ['proxy', 'start']);
  appDataServiceSpy.start.and.returnValue(of(id));
  appDataServiceSpy.proxy = proxySpy;

  component.appDataService = appDataServiceSpy;

  component.delete(post);

  flush();

  // ✔️ this expectation is passes
  expect(component.appDataService.proxy.delete)
    .withContext('appDataService.proxy.delete')
    .toHaveBeenCalledTimes(1);

  // ✔️ this expectation is passes
  expect(component.appDataService.proxy.delete)
    .withContext('appDataService.proxy.delete')
    .toHaveBeenCalledWith(post, component.paginate);

  // ✔️ this expectation is passes
  expect(component.appDataService.start)
    .withContext('appDataService.start')
    .toHaveBeenCalledTimes(1);

  // ❌ this expectation is fail
  expect(component.loadDatas).toHaveBeenCalledTimes(1);
}));

The last expectation fails with this message:

Expected spy loadDatas to have been called once. It was called 0 times.

I think the switchMap causes the issue, but I don't know why. I tried to apply flushMicrotasks(), tick() on above test code, and combined with fixture.whenStabel() without success.

Any ide how can I solve this issue?

And I would also be grateful for an explanation.


Solution

  • You have to use done() instead of fakeAsync. The fakeAsync will wait, but the subscription will not be finished. The done() function will "trigger" the rxjs operators, and you can test the result in a subscibe to the observer.

    I think just like this:

    it('should call proxy.delete with model on call delete', (done) => {
      ...
      component.delete(post);
    
      component.appDataService.start.subscribe(() => {
        expect(component.appDataService.proxy.delete)
          .withContext('appDataService.proxy.delete')
          .toHaveBeenCalledTimes(1);
        ...
        expect(component.loadDatas).toHaveBeenCalledTimes(1);
      }
    
      done();
    }