迁移到 Angular 1.6.3 后无法测试被拒绝的承诺

Unable to test a rejected promise after migrating to Angular 1.6.3

我最近将我的应用程序从 Angular 1.5 更新到 1.6.3,并开始在我编写的基于承诺的代码周围出现 Jasmine 单元测试失败(使用 PhantomJS):

Possibly unhandled rejection: undefined thrown

阅读周围我发现公认的解决方案是将 .then() 与 .catch() 块链接起来以优雅地处理拒绝。

我已经对我正在测试的一个源文件进行了此操作,以证明它已解决了它所犯的错误。

但是,它现在发现了一个进一步的问题,即当我的代码中调用承诺拒绝时我正在测试的期望不再通过。

这是我要测试的功能(在添加所需的 catch 块之后)

public deleteSomething = (thing) => {
    return this.UserMessages.buildConfirmDialog().then(() => {
        this.someService.remove(thing)
            .then(() => {
                this.UserMessages.showToast('Something deleted');
            })
            .catch((error) => {
                //handle error
            });
    })
    .catch((error) => {
        //handle error
    });
}

这是测试:

var thing = {foo: 'bar'},
    deferredRemove,
    deferredConfirm,
    //Mock service below injected into controller later on before test are run
    UserMessages = {
        buildConfirmDialog: jasmine.createSpy('buildConfirmDialog').and.callFake(function() {
            deferredConfirm = $q.defer();
            return deferredConfirm.promise.catch(angular.noop);
        })
    };

//Inject and controller setup here...

    describe('When deleting something', function() {
        beforeEach(function() {
            deferredRemove = $q.defer();
            spyOn(someService, 'remove').and.returnValue(deferredRemove.promise.catch(angular.noop));
        });
        describe('and the user confirms the deletion', function() {
            beforeEach(function() {
                ctrl.deleteSomething(thing);
                deferredConfirm.resolve();
                deferredRemove.resolve();
                $rootScope.$apply();
            });
            it('should call remove on someService', function() {
                console.log('someService.remove.calls = ' + someService.remove.calls.count());
                expect(someService.remove).toHaveBeenCalled();
            });
        });
        describe('and the user cancels the deletion', function() {
            beforeEach(function() {
                someService.remove.calls.reset();
                vm.deleteSomething(thing);
                deferredConfirm.reject({});
                $rootScope.$apply();
            });
            it('should not call remove on someService', function() {
                console.log('someService.remove.calls = ' + someService.remove.calls.count());
                expect(someService.remove.calls.count()).toEqual(0);
            });
        });
    });

在升级到 1.6.3 之前我没有 .catch(angular.noop) 部分,并且遇到了一些建议这样做以使测试愉快的帖子,这无疑帮助我克服了未处理的问题我的测试中出现拒绝错误 运行.

我现在面临的问题是,对于 reject 测试规范,我的服务中不应该调用 remove 函数,因此调用次数应该为零,但它一直显示为 1 . 我在我的测试中添加了重置调用的行,以确保它不是以前的测试贡献的(我知道调用应该在测试之间重置)。

当我使用 1.5 时,这个测试 运行ning 很好,所以这一定是我的 code\test 编写的方式不能很好地适应 1.[=38 中的变化=]

有人可以阐明这里可能发生的事情吗?

谢谢

通过此配置禁用可能未处理的拒绝并再次测试。

app.config(['$qProvider', function ($qProvider) {
    $qProvider.errorOnUnhandledRejections(false);
}]);

我在升级到 1.6.3 之前没有 .catch(angular.noop) 部分,并且看到一些建议这样做以使测试愉快的帖子,这对我当然有帮助在我的测试中克服未处理的拒绝错误 运行.

添加.catch(angular.noop)肯定会处理未处理的拒绝。

它将拒绝的承诺转换为已履行的承诺!

你的测试正确地失败了,因为你破坏了你的代码。

有关详细信息,请参阅


AngularJS V1.6

对 $q 的更改

使用非拒绝回调报告承诺

Rejected promises that do not have a callback to handle the rejection report this to $exceptionHandler so they can be logged to the console.

BREAKING CHANGE

Unhandled rejected promises will be logged to $exceptionHandler.

Tests that depend on specific order or number of messages in $exceptionHandler will need to handle rejected promises report.

将抛出的错误视为常规拒绝

Previously, errors thrown in a promise's onFulfilled or onRejected handlers were treated in a slightly different manner than regular rejections: They were passed to the $exceptionHandler() (in addition to being converted to rejections).

The reasoning for this behavior was that an uncaught error is different than a regular rejection, as it can be caused by a programming error, for example. In practice, this turned out to be confusing or undesirable for users, since neither native promises nor any other popular promise library distinguishes thrown errors from regular rejections. (Note: While this behavior does not go against the Promises/A+ spec, it is not prescribed either.)

This commit removes the distinction, by skipping the call to $exceptionHandler(), thus treating thrown errors as regular rejections.

Note: Unless explicitly turned off, possibly unhandled rejections will still be caught and passed to the $exceptionHandler(), so errors thrown due to programming errors and not otherwise handled (with a subsequent onRejected handler) will not go unnoticed.

有关详细信息,请参阅 AngularJS Developer Guide - Migrating from V1.5 to V1.6