Promise.catch()在AngularJS单元测试中没有捕获异常

Imp*_*rks 11 unit-testing jasmine angularjs typescript

我正在为Typescript中的应用程序编写Jasmine单元测试并通过Resharper运行它们.如果处理程序抛出异常,它应该执行一个动作:

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');
    });
});
Run Code Online (Sandbox Code Playgroud)

但是,测试失败了:

Resharper测试失败

这是我的测试环境/工具的一些奇怪的行为,还是我错误地使用了promise机制本身?

rye*_*lar 15

您肯定错误地使用了promise机制,抛出用户定义的throw语句并不构成将其作为承诺拒绝正确使用.如$q文档中所述:

在将deferreds/promises与熟悉的try/catch/throw行为进行比较时,请将reject视为JavaScript中的throw关键字.这也意味着如果您通过promise错误回调"捕获"错误并且您希望将错误转发到从当前promise中派生的promise,则必须通过返回通过reject构造的拒绝来"重新抛出"错误.

它们类似但不等同,为了捕获用户定义的throw语句,你应该使用catch语句块.虽然$q承诺应该只catch拒绝承诺.因此,在您的情况下,返回拒绝的promise是处理进程的正确方法,而不是抛出用户定义的异常.

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

  });

});
Run Code Online (Sandbox Code Playgroud)

更新:

您的代码在浏览器中以这种方式运行的原因是因为Angular的$q实现try/catch在处理promise队列时使用了语句块.当任何回调抛出任何错误时,它会捕获错误本身,以异常作为拒绝原因拒绝它,之后它用于$exceptionHandler记录错误.我建议你简单地回复被拒绝的承诺.

至于单元测试这样做的原因是因为angular-mocks实现$exceptionHandler与实际应用程序不同$exceptionHandler.前者创建一个具有不同模式的提供程序,默认的角度模拟实现使用rethrow模式,而模式又抛出异常而不是记录它.如果您希望单元测试的行为与默认应用程序的行为方式相同,$exceptionHandler则可以将模式设置为'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');    

  });

});
Run Code Online (Sandbox Code Playgroud)