Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Before migrating to RxJs6, one of my unit tests was :

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

In my component, I subscribe to the subject and call a private method that can throw an exception. Something looking like that :

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

Since I migrated to RxJs6 this UT does not work anymore (it worked before) and I cannot figure out how to make it work.

I read the migration guide, especially this section : Replacing synchronous error handling, but it is about subscribe(), not next()...

Thanks in advance

like image 692
M'sieur Toph' Avatar asked May 14 '18 15:05

M'sieur Toph'


2 Answers

That's correct. In RxJS 5 when subscribing with subscribe if you didn't set any error handler the error was just re-thrown. That's why your unit test worked before.

But that's not how it works in RxJS 6 because all non-handled errors are re-thrown in window.onerror or process.on('error') (depending on your environment).

What you could do is making the test async and then checking that one of the handlers above was called:

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

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

This is mocha style but I guess in Jasmine it's going to be similar.

Actually, what you have is not a very good way to test observable chains because whether an error is handled or not is only subscribers business and not caller's. In other words, you shouldn't test how an emission is handled by subscribers.

It took me a while to find the appropriate commit but read the description here https://github.com/ReactiveX/rxjs/commit/cd9626a4f93cac6f631d5a97dd9c9b2aa8e4b5db (it's mentioned also in the CHANGELOG.md).

like image 120
martin Avatar answered Sep 27 '22 21:09

martin


I found a workaround.

Not sure about the relevance, but it seems to work for me.

I use the angular testing methods fakeAsync and tick to trigg the emission of the unhandled exception.

Transform :

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

into :

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

By the way, this trick also makes me sure that if the exception is not thrown, the test will fail.

like image 21
M'sieur Toph' Avatar answered Sep 27 '22 21:09

M'sieur Toph'