Angular - 测试组件 - 从模拟返回 Promise.reject 时出错

daf*_*rys 8 javascript unit-testing promise angular-components angular

我有一个组件单元测试,它没有按照我预期的方式处理来自模拟的承诺拒绝。

我在一个组件上有这个函数,它发送一些数据addUserToOrganisation并处理它返回的 Promise:

 public onSubmit() {
    this.saveStatus = 'Saving';
    this.user = this.prepareSaveUser();
    this._userService.addUserToOrganisation(this.user)
    .then(() => this._router.navigate(['/profile']))
    .catch(error => this.reportError(error));
  }
Run Code Online (Sandbox Code Playgroud)

在测试此组件时,我提供了一个模拟,用于UserService监视端点addUserToOrganisation并返回某种 Promise:

 mockUserService = jasmine.createSpyObj('mockUserService', ['getOrgId', 'addUserToOrganisation']);
 mockUserService.getOrgId.and.returnValue(Promise.resolve('an id'));
 mockUserService.addUserToOrganisation.and.returnValue(Promise.resolve());
Run Code Online (Sandbox Code Playgroud)

这对于快乐的路径(解决)来说效果很好 - 我可以测试它this._router.navigate()被调用等等。这是这条快乐路径的通过测试:

it('should navigate to /profile if save is successful', fakeAsync(() => {
    fixture.detectChanges();
    tick();
    fixture.detectChanges();

    component.userForm.controls['firstName'].setValue('John');
    component.userForm.controls['lastName'].setValue('Doe');
    component.userForm.controls['email'].setValue('j.d@gmail.com');
    component.onSubmit();

    tick();
    fixture.detectChanges();
    expect(mockRouter.navigate).toHaveBeenCalledWith(['/profile']);
  }));
Run Code Online (Sandbox Code Playgroud)

然而,我在测试“悲伤”路径时遇到了麻烦。我改变我的模拟以返回 a Promise.reject,尽管我有.catchin onSubmit,但我收到此错误:

Error: Uncaught (in promise): no
Run Code Online (Sandbox Code Playgroud)

所以这很令人困惑。这是我对这条悲伤之路的测试。请注意,我更改了模拟调用的响应。

it('should show Failed save status if the save function fails', fakeAsync(() => {
    mockUserService.addUserToOrganisation.and.returnValue(Promise.reject('no'));
    fixture.detectChanges();
    tick();
    fixture.detectChanges();

    component.userForm.controls['firstName'].setValue('John');
    component.userForm.controls['lastName'].setValue('Doe');
    component.userForm.controls['email'].setValue('j.d@gmail.com');
    component.onSubmit();

    tick();
    fixture.detectChanges();

    expect(component.saveStatus).toEqual('Failed! no');
  }));
Run Code Online (Sandbox Code Playgroud)

有人有什么想法吗?

Est*_*ask 8

Promise 拒绝应该被捕获,不处理它们将导致 Zone.js Promise 实现(在 Angular 应用程序中使用)和其他一些(Chrome、core-js Promise Polyfill 等)中出现错误。

应同步捕获拒绝才能视为已处理。这样可以保证错误总是得到处理。

这是处理承诺:

const p = Promise.reject();
p.catch(err => console.error(err));
Run Code Online (Sandbox Code Playgroud)

这是未处理的承诺:

const p = Promise.reject();
setTimeout(() => {
  p.catch(err => console.error(err));
}, 1000);
Run Code Online (Sandbox Code Playgroud)

即使将来可能会处理拒绝,但实现无法“知道”这一点,因此承诺被视为未处理并unhandledrejection触发事件。

造成问题的原因是

tick();
fixture.detectChanges();
Run Code Online (Sandbox Code Playgroud)

如果tick()存在“以防万一”并且没有实际用途,那么它首先就不应该存在。如果应该的话,则需要更改代码以不创建未处理的承诺:

mockUserService.addUserToOrganisation.and.callFake(() => Promise.reject('no'));
Run Code Online (Sandbox Code Playgroud)

  • `callFake` 在调用时创建了一个被拒绝的 Promise,因此它在 `component.onSubmit()` 调用期间与 `.catch` 链接并导致处理的 Promise。`returnValue` 接受现有被拒绝的承诺,该承诺应该立即与 `.catch` 链接,但事实并非如此 - 因为在 `component.onSubmit()` 中捕获拒绝之前,计时器已通过 `tick()` 移动到下一个刻度。`setTimeout` 的例子准确地展示了那里发生的事情。它应该在没有 `tick()` 的情况下工作。我不能说为什么没有它你仍然会收到此错误。 (2认同)