问题
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?
回答1:
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.
来源:https://stackoverflow.com/questions/58429612/mocking-matsnackbar-in-angular-8-jasmine