Angular/RxJS 6 - 如何对 next() 触发的指令抛出异常进行单元测试

Angular/RxJS 6 - How to unit-test that instructions triggered by a next() throw an exception

在迁移到 RxJs6 之前,我的一个单元测试是:

it('should do what I expect, () => {
  expect(() => {
    myComponent.mySubject.next({message: 'invalid'});
  }).toThrow('invalid is not an accepted message');
})

在我的组件中,我订阅了主题并调用了一个可以抛出异常的私有方法。看起来像那样的东西:

export class MyComponent {
  //...
  mySubject = new Subject();
  //...
  ngOnInit(){
    this.mySubject.subscribe(obj => this._doSomething(obj))
  }
  //...
  private _doSomething(obj) {
    if ('invalid' === obj.message) {
      throw new Error('invalid is not an accepted message');
    }
    //...
  }
}

自从我迁移到 RxJs6 之后,这个 UT 就不再工作了(以前工作过),我不知道如何让它工作。

我阅读了迁移指南,尤其是这一部分:Replacing synchronous error handling,但它是关于 subscribe(),而不是 next()...

提前致谢

没错。在 RxJS 5 中,当使用 subscribe 订阅时,如果您没有设置任何 error 处理程序,则会重新抛出错误。这就是为什么您的单元测试之前有效的原因。

但这不是它在 RxJS 6 中的工作方式,因为所有未处理的错误都会在 window.onerrorprocess.on('error') 中重新抛出(取决于您的环境)。

您可以做的是使测试异步,然后检查上面的处理程序之一是否被调用:

it('should do what I expect, done => {
  process.once('error', () => done());

  myComponent.mySubject.next({message: 'invalid'});
});

这是 mocha 风格,但我想在 Jasmine 中它会很相似。

实际上,您所拥有的并不是测试可观察链的好方法,因为是否处理错误只是订阅者的事,而不是调用者的事。换句话说,您不应该测试订阅者如何处理发射。

我花了一段时间才找到合适的提交,但请阅读此处的描述 https://github.com/ReactiveX/rxjs/commit/cd9626a4f93cac6f631d5a97dd9c9b2aa8e4b5db (it's mentioned also in the CHANGELOG.md)。

我找到了解决方法。

不确定相关性,但它似乎对我有用。

我使用 angular 测试方法 fakeAsynctick 来触发未处理异常的发射。

变换:

it('should do what I expect, () => {
  expect(() => {
    myComponent.mySubject.next({message: 'invalid'});
  }).toThrow('invalid is not an accepted message');
})

进入:

it('should do what I expect, fakeAsync(() => {
  myComponent.mySubject.next({message: 'invalid'});
  expect(() => tick())
    .toThrow('invalid is not an accepted message');
}))

顺便说一句,这个技巧也让我确定如果不抛出异常,测试就会失败。