简体   繁体   中英

Mocking MatSnackBar in Angular 8 & Jasmine

I have an Angular 8 application that uses the Angular Material MatSnackBar and I am trying to test that the open() method of the class is called. The call to the open() method is within the body of an NgRx store selector, like this:

ngOnInit() {
  this.store.dispatch(fromStore.getFeaturedPlaylists());

  this.subscription = this.store.pipe(
    select(fromStore.selectError),
    filter(err => !!err),
    switchMap((err) => this.snackBar.open(
      `Error: ${err.message}`,
      'Try again',
      {duration: 5000}).afterDismissed())
  ).subscribe(() => this.store.dispatch(fromStore.getFeaturedPlaylists()));
}

The relevant part of my test looks like this:

describe('FeaturedPlaylistsComponent', () => {

  let component: FeaturedPlaylistsComponent;
  let fixture: ComponentFixture<FeaturedPlaylistsComponent>;
  let matSnackBarSpy: jasmine.SpyObj<MatSnackBar>;

  beforeEach(async(() => {
    const spy = jasmine.createSpyObj('MatSnackBar', ['open']);

    TestBed.configureTestingModule({
      declarations: [
        // other declarations here
        FeaturedPlaylistsComponent
      ],
      providers: [
        // other providers here
        {provide: MatSnackBar, useValue: spy}
      ]
    })
    .compileComponents();

    matSnackBarSpy = TestBed.get<MatSnackBar>(MatSnackBar);
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(FeaturedPlaylistsComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  describe('#ngOnInit()', () => { // fails
    // lots of other test...
    it('should call MatSnackBar.open() on error', () => {
      const error = new HttpErrorResponse({error: 'Some error'});

      component.ngOnInit();
      store.dispatch(fromStore.setError({error}));

      expect(matSnackBarSpy.open).toHaveBeenCalled();
    });
  });

});

Now I know that this ngOnInit() function is working because I have another test that tests that the getFeaturedPlayists() action is dispatched twice: once on the first line of the function and once in the subscribe block:

it('should dispatch a getFeaturedPlaylists() action on error', () => { // succeeds
  const spy = spyOn(store, 'dispatch').and.callThrough();
  const error = new HttpErrorResponse({error: 'Some error'});

  component.ngOnInit();
  store.dispatch(fromStore.setError({error}));

  expect(spy).toHaveBeenCalledTimes(2);
  expect(spy.calls.allArgs()).toEqual([
    [fromStore.getFeaturedPlaylists()],
    [fromStore.setError({error})]
  ]);
});

To be honest I'm surprised that this test works: I would have assumed that I would need a tick(5000) to wait for the dialog to be dismissed, so perhaps there is actually something more sinister going on here.

Another thing: if I move the open() call to the first line of the function, like this:

ngOnInit() {
  this.snackBar.open(
    `Error: Some error`,
    'Try again',
    {duration: 5000});
  // this.store.dispatch(fromStore.getFeaturedPlaylists());

  // this.subscription = this.store.pipe(
  //   select(fromStore.selectError),
  //   filter(err => !!err),
  //   switchMap((err) => this.snackBar.open(
  //     `Error: ${err.message}`,
  //     'Try again',
  //     {duration: 5000}).afterDismissed())
  // ).subscribe(() => this.store.dispatch(fromStore.getFeaturedPlaylists()));
}

the should call MatSnackBar.open() on error test works, so my understanding is that it is something to do with the call to open() being within this store selector.

Can anyone tell me why the should dispatch a getFeaturedPlaylists() action on error test works and the should call MatSnackBar.open() on error test doesn't?

Ok, I realized that I was using a mock store so no selectors were actually working. Using the real store for these tests, and with a little fiddling, I got them to work.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM