Promise.catch() does not catch exception in AngularJS unit test

拟墨画扇 提交于 2019-12-10 02:14:10

问题


I am writing Jasmine unit tests for my app in Typescript and running them via Resharper. It is supposed to execute an action if the handler throws an exception:

describe("Q Service Test", () => {
    var q: ng.IQService;
    var rootScope: ng.IRootScopeService;

    beforeEach(inject(($q, $rootScope) => {
        q = $q;
        rootScope = $rootScope;
    }));

    it("Caught exceptions are handled properly", () => {
        var state = 'ok';
        q.when(1)
            .then(() => {
                throw new Error("test exception");
            })
            .catch(() => {
                state = 'error';
            });

        rootScope.$digest();
        expect(state).toBe('error');
    });
});

However, the test is failed:

Is it some strange behaviour of my testing environment / tools, or am I incorrectly using the promise mechanism itself?


回答1:


You are definitely using the promise mechanism incorrectly, throwing a user-defined throw statement does not constitute to catching it as a promise rejection properly. As stated in the $q documentation:

When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of reject as the throw keyword in JavaScript. This also means that if you "catch" an error via a promise error callback and you want to forward the error to the promise derived from the current promise, you have to "rethrow" the error by returning a rejection constructed via reject.

They are similar but not equivalents, to catch user-defined throw statements, you should use catch statement blocks. While $q promises should only catch rejected promises. Thus, in your case, returning a rejected a promise is the proper way of handling the process instead of throwing a user-defined exception.

DEMO

JAVASCRIPT

describe('Q Service Test', function() {

  var $q,
      $rootScope;

  beforeEach(inject(function(_$q_, _$rootScope_) {
    $q = _$q_;
    $rootScope = _$rootScope_;
  }));

  it('Rejected promises are handled properly', function() {

    var state = 'ok';

    $q.when(1)
      .then(function() {
        return $q.reject('rejected');
      })
      .catch(function() {
        state = 'error';
      });

    $rootScope.$digest();
    expect(state).toBe('error');    

  });

});

UPDATE:

The reason why your code behaves this way in the browser, is because Angular's $q implementation uses the try/catch statement blocks in processing the promise queue. When any of the callbacks throw any errors, it catches the error itself, rejects it with the exception as a reason for rejection, afterwards it uses $exceptionHandler to log the error. I recommend you to simply return rejected promise.

As for the reason why the unit tests act this way is because of angular-mocks implementation of the $exceptionHandler is different with the actual application's $exceptionHandler. The former creates a provider with different modes, the default angular-mocks implementation uses the rethrow mode which in turn throws the exception instead of logging it. If you want to have your unit tests behave the same way as how the default application's $exceptionHandler then you can set the mode to 'log'.

DEMO

JAVASCRIPT

describe('Q Service Test', function() {

  var $q,
      $rootScope;

  beforeEach(module('ng', function($exceptionHandlerProvider) {
    $exceptionHandlerProvider.mode('log');
  }));

  beforeEach(inject(function(_$q_, _$rootScope_) {
    $q = _$q_;
    $rootScope = _$rootScope_;
  }));

  it('Caught exceptions are handled properly', function() {

    var state = 'ok';

    $q.when(1)
      .then(function() {
        throw new Error();
      })
      .catch(function() {
        state = 'error';
      });

    $rootScope.$digest();
    expect(state).toBe('error');    

  });

});



回答2:


The blog post Using and chaining promises in AngularJS mentions that when you throw and exception it "will also trigger Angular's registered exception handler". So my guess is that Jasmine is using Angular's exception handler to listening for exceptions.

Do you need to throw an exception or could you just do something like this:

return q.reject(new Error("test exception"));


来源:https://stackoverflow.com/questions/31538364/promise-catch-does-not-catch-exception-in-angularjs-unit-test

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!