我正在为Typescript中的应用编写Jasmine单元测试并通过Resharper运行它们。如果处理程序抛出异常,它应该执行一个动作:
{{1}}
然而,测试失败了:
我的测试环境/工具有些奇怪的行为,还是我错误地使用了promise机制?
答案 0 :(得分:15)
您肯定使用承诺机制错误,抛出用户定义的抛出语句并不构成将其作为承诺拒绝正确使用。如$q
文档中所述:
将延迟/承诺与熟悉的行为进行比较 try / catch / throw,将拒绝视为JavaScript中的 throw 关键字。 这也意味着如果您通过承诺错误“捕获”错误 回调,您希望将错误转发给派生自的承诺 当前的承诺,你必须通过返回一个“重新抛出”错误 通过拒绝构建拒绝。
它们类似但不等同,为了捕获用户定义的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');
});
});
<强>更新强>
您的代码在浏览器中以这种方式运行的原因是因为Angular的$q
实现在处理promise队列时使用try/catch
语句块。当任何回调抛出任何错误时,它会捕获错误本身,以异常作为拒绝原因拒绝它,之后它使用$exceptionHandler
来记录错误。我建议你简单地回复被拒绝的承诺。
至于单元测试以这种方式行事的原因是因为angular-mocks
$exceptionHandler
的实现与实际应用程序的 {{3}不同} 即可。前者创建了一个具有不同模式的提供程序,默认的角度模拟实现使用rethrow
模式,而模式又抛出异常而不是记录它。如果您希望单元测试的行为与默认应用程序$exceptionHandler
的行为方式相同,那么您可以将模式设置为'log'
。
<强> $exceptionHandler
强>
<强> 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');
});
});
答案 1 :(得分:2)
博文Using and chaining promises in AngularJS提到,当你抛出异常时,它还会触发Angular的注册异常处理程序&#34;。所以我的猜测是Jasmine正在使用Angular的异常处理程序来监听异常。
你是否需要抛出异常,或者你可以做这样的事情:
return q.reject(new Error("test exception"));