ngxs:store.reset禁用茉莉单元测试中的调度员

发布于 2025-02-09 16:31:42 字数 2911 浏览 2 评论 0原文

在用茉莉花测试我的NGXS商店时,我的出乎意料的行为遇到了麻烦。

在这里,我试图测试DeleteAlerts操作:

  @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.');
          }
        }
      ));
  }

但是首先必须使用虚拟数据填充该商店。

我创建了此模型:

  const alertsMock = new Alerts({
alerts: [new UniqueAlert({alertIds: ['test1']}),
  new UniqueAlert({alertIds: ['test2']}),
  new UniqueAlert({alertIds: ['test3']})]
  });

我的商店看起来像这样:

 export interface AlertStateModel {
  alerts: UniqueAlert[];
}

然后我尝试用模拟填充商店:

store.reset({alerts: alertsMock.alerts})

但是,当我在测试中执行此操作时,deletealerts操作在调用时不会派遣。 store.dispatch(new deletealerts(alertids))

这是我不明白的部分:用getAlerts store.reset方法时,操作确实派遣了。 >调度,指示从模拟服务加载警报:

getAlerts操作:

  @Action(GetAlerts)
  public getAlerts(ctx: StateContext<AlertStateModel>) {
    return this.alertService.getAlerts().pipe(
      tap(fetchedAlerts => {
        ctx.setState({alerts: fetchedAlerts.alerts});
      })
    );
  }

此测试通过:

  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')));
    });
  });
});

此测试没有:

  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')));
    });
  });

最重要的是,您可能已经注意到我的期望是在data上。 ,而不是data.Alerts在非功能测试中。这是我想理解的另一种行为,因为选择器应返回状态,该状态包含嵌套arters对象。

为什么两项测试不等效,为什么选择器使用store.reset填充商店的预期对象

? 包含一个嵌套警报对象;这就是AlertService返回的数据的格式。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

や三分注定 2025-02-16 16:31:42

看起来您没有将商店快照扩展到测试重置中。该文档并没有明确说明这样做,但是当我以这种方式重置时,我自己的测试通过了。

describe('the test suite', () => {
  let store: Store;
  beforeEach(() => {
    store = TestBed.inject(Store);
    store.reset({ ...store.snapshot(),
      DESIRED_STATE
    })
  });
  
  it('must do the test', () => {
    store.reset({ ...store.snapshot(),
      NEW_DESIRED_STATE
    })
  });
});

It looks like you are not expanding the store snapshot into the test reset. The documentation doesn't explicitly say to do this, but my own tests pass when I reset in this fashion.

describe('the test suite', () => {
  let store: Store;
  beforeEach(() => {
    store = TestBed.inject(Store);
    store.reset({ ...store.snapshot(),
      DESIRED_STATE
    })
  });
  
  it('must do the test', () => {
    store.reset({ ...store.snapshot(),
      NEW_DESIRED_STATE
    })
  });
});

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文