I am having trouble with an unexpected behavior while unit testing my NGXS store with Jasmine.
Here, I am trying to test the DeleteAlerts action :
@Action(DeleteAlerts)
deleteAlerts(ctx: StateContext<AlertStateModel>, action: DeleteAlerts) {
return this.alertService.deleteAlerts(action.alertIds).pipe(
map(response => {
if (response.ok) {
action.alertIds.forEach(alertId => {
ctx.setState(patch({
alerts: removeItem<UniqueAlert>(alert => alert.alertIds.includes(alertId))
}));
});
} else {
throw new Error('Server failed to respond.');
}
}
));
}
but the store must first be populated with dummy data.
I created this mock :
const alertsMock = new Alerts({
alerts: [new UniqueAlert({alertIds: ['test1']}),
new UniqueAlert({alertIds: ['test2']}),
new UniqueAlert({alertIds: ['test3']})]
});
My store looks like this :
export interface AlertStateModel {
alerts: UniqueAlert[];
}
I then tried to populate the store with the mock :
store.reset({alerts: alertsMock.alerts})
However, when I do this in my test, the DeleteAlerts action does not dispatch when calling store.dispatch(new DeleteAlerts(alertIds))
Here is the part I do not understand : The action does dispatch when replacing the store.reset method with a GetAlerts dispatch, which is directed to load the alerts from a mocked service :
The GetAlerts action :
@Action(GetAlerts)
public getAlerts(ctx: StateContext<AlertStateModel>) {
return this.alertService.getAlerts().pipe(
tap(fetchedAlerts => {
ctx.setState({alerts: fetchedAlerts.alerts});
})
);
}
This test passes :
it('should delete one alert from the store when DeleteAlerts is dispatched', () => {
spyOn(alertService, 'getAlerts').and.returnValue(of(alertsMock));
store.dispatch(new GetAlerts());
spyOn(alertService, 'deleteAlerts').and.returnValue(of(new HttpResponse({status: 200})));
store.dispatch(new DeleteAlerts(['test2']));
store.selectOnce(AlertState).subscribe(data => {
expect(data.alerts).toEqual(alertsMock.alerts.filter(alert => !alert.alertIds.includes('test2')));
});
});
});
This test does not :
it('should delete one alert from the store when DeleteAlerts is dispatched', () => {
store.reset({alerts: alertsMock.alerts});
spyOn(alertService, 'deleteAlerts').and.returnValue(of(new HttpResponse({status: 200})));
store.dispatch(new DeleteAlerts(['test2']));
store.selectOnce(AlertState).subscribe(data => {
expect(data).toEqual(alertsMock.alerts.filter(alert => !alert.alertIds.includes('test2')));
});
});
On top of that, you might have noticed that my expectation is on data, rather than data.alerts on the non-functional test. This is another behavior I would like to understand, since the selector should return the state, which contains a nested alerts object.
Why aren't the two tests equivalent, and why does the selector not return the expected object when using store.reset to populate the store ?
Regarding why the alertsMock contains a nested alerts object ; that is the format of the data returned by the alertService.