Javascript 角度测试组件-从模拟返回PROMITE.reject时出错

Javascript 角度测试组件-从模拟返回PROMITE.reject时出错,javascript,angular,unit-testing,promise,angular-components,Javascript,Angular,Unit Testing,Promise,Angular Components,我有一个组件单元测试,它没有以我预期的方式处理来自模拟的承诺拒绝。 我在一个组件上有一个函数,它将一些数据发送到addUserToOrganization,并处理它返回的承诺: public onSubmit() { this.saveStatus = 'Saving'; this.user = this.prepareSaveUser(); this._userService.addUserToOrganisation(this.user) .then(()

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

我在一个组件上有一个函数,它将一些数据发送到
addUserToOrganization
,并处理它返回的承诺:

 public onSubmit() {
    this.saveStatus = 'Saving';
    this.user = this.prepareSaveUser();
    this._userService.addUserToOrganisation(this.user)
    .then(() => this._router.navigate(['/profile']))
    .catch(error => this.reportError(error));
  }
在测试此组件时,我为
UserService
提供了一个mock,它监视
addusertoorganization
端点并返回某种承诺:

 mockUserService = jasmine.createSpyObj('mockUserService', ['getOrgId', 'addUserToOrganisation']);
 mockUserService.getOrgId.and.returnValue(Promise.resolve('an id'));
 mockUserService.addUserToOrganisation.and.returnValue(Promise.resolve());
这适用于快乐路径(解析)-我可以测试
这一点。\u 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']);
  }));
然而,我在测试“悲伤”路径时遇到了困难。我更改我的mock以返回一个
承诺。拒绝
,尽管我在
onSubmit
中有一个
.catch
,但我收到了以下错误:

Error: Uncaught (in promise): no
所以这很令人困惑。这是我对这条悲惨道路的考验。注意,我更改了模拟调用的响应

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

有人有什么想法吗?

承诺拒绝应该被捕获,如果不进行处理,将导致Zone.js Promise实现(用于Angular应用程序)和其他一些(Chrome、core js Promise polyfill等)出错

应同步捕获拒绝,以便视为已处理。通过这种方式可以保证始终会处理错误

这是我的承诺:

const p = Promise.reject();
p.catch(err => console.error(err));
const p = Promise.reject();
setTimeout(() => {
  p.catch(err => console.error(err));
}, 1000);
这是未经处理的承诺:

const p = Promise.reject();
p.catch(err => console.error(err));
const p = Promise.reject();
setTimeout(() => {
  p.catch(err => console.error(err));
}, 1000);
即使将来可能会处理拒绝,但实现无法“知道”这一点,因此承诺被视为未处理,并触发
unhandledrejection
事件

造成问题的原因是

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

mockUserService.addUserToOrganisation.and.callFake(() => Promise.reject('no'));

我试图删除勾号(),但仍然出现错误。我不太明白为什么没有必要?但是,将模拟响应从
returnValue…
更改为
callFake…
已经完成了这项任务。不过,我认为我没有100%理解这里的细微差别。
callFake
在调用时创建一个被拒绝的承诺,因此它在
组件期间与
.catch
链接。onSubmit()
调用并产生已处理的承诺
returnValue
接受现有被拒绝的承诺,该承诺应立即用
链接。catch
,但事实并非如此-因为在
组件中捕获拒绝之前,计时器会用
tick()
移动到下一个tick。带有
setTimeout
的示例准确地显示了那里发生了什么。它应该在没有勾选(
的情况下工作。我说不出为什么没有它你仍然会出现这个错误。@EstusFlask它对我来说很好,但它没有覆盖我代码中的catch块。通过覆盖Promise.resolve和Promise,我还希望该方法的代码覆盖率达到100%。reject@PritamBohra您的代码可能与发布的代码不同。我希望
.and.callFake(()=>Promise.reject('no'))
捕获提供覆盖范围。