简体   繁体   English

unit testing angularjs $ q.all - promise永远不会完成

[英]unit testing angularjs $q.all - promise never completes

I'm trying to test a service that I build which uses Angular's $q implementation of Promises. 我正在尝试测试我构建的服务,该服务使用Angular的$ q Promise实现。 I'm using a combination of Karma, Mocha, Chai, Sinon, Sinon Chai and Chai as Promised. 我正在使用Karma,Mocha,Chai,Sinon,Sinon Chai和Chai的组合作为承诺。

All the tests that I wrote and return promises are passing but the ones that reject or uses $q.all([ ... ]) . 我写的所有测试和返回的承诺都是通过但拒绝或使用$q.all([ ... ]) I have tried all I could think of but I cannot seem to find where the issue is. 我已经尝试了所有我能想到的但是我似乎无法找到问题所在。

The following is a slim version of what I am testing: 以下是我正在测试的超薄版本:

"use strict";


describe("Promise", function () {

    var $rootScope,
        $scope,
        $q;

    beforeEach(angular.mock.inject(function (_$rootScope_, _$q_) {
        $rootScope = _$rootScope_;
        $q = _$q_;
        $scope = $rootScope.$new();
    }));

    afterEach(function () {
        $scope.$apply();
    });

    it("should resolve promise and eventually return", function () {

        var defer = $q.defer();

        defer.resolve("incredible, this doesn't work at all");

        return defer.promise.should.eventually.deep.equal("incredible, this doesn't work at all");
    });

    it("should resolve promises as expected", function () {

        var fst = $q.defer(),
            snd = $q.defer();

        fst
            .promise
            .then(function (value) {
                value.should.eql("phew, this works");
            });

        snd
            .promise
            .then(function (value) {
                value.should.eql("wow, this works as well");
            });

        fst.resolve("phew, this works");
        snd.resolve("wow, this works as well");

        var all = $q.all([
            fst.promise,
            snd.promise
        ]);

        return all.should.be.fullfiled;
    });

    it("should reject promise and eventually return", function () {
        return $q.reject("no way, this doesn't work either?").should.eventually.deep.equal("no way, this doesn't work either?");
    });

    it("should reject promises as expected", function () {

        var promise = $q.reject("sadly I failed for some stupid reason");

        promise
            ["catch"](function (reason) {
                reason.should.eql("sadly I failed for some stupid reason");
            });

        var all = $q.all([
            promise
        ]);

        return all.should.be.rejected;
    });

});

The 3rd, last and the first test are the ones that fail. 第三次,最后一次和第一次测试是失败的。 Actually it does not fail, it just resolves after the timeout is exceeded and I get a Error: timeout of 2000ms exceeded . 实际上它没有失败,它只是在超过超时后才解决,我得到一个Error: timeout of 2000ms exceeded

EDIT : I have just tried to test with Kris Kowal 's implementation of the promises and it works just fine with that. 编辑 :我刚刚尝试用Kris Kowal对承诺的实施进行测试,它的工作原理很好。

PS I actually found that there is some time spent somewhere in the bowls of either Mocha, Chai or Chai As Promised and the afterEach hook gets called later than the timeout. PS 我实际上发现在Mocha,Chai或Chai As Promised的碗里有一些时间,并且afterEach钩子在超时之后被调用。

afterEach() is used for cleanup, not for executing code after your preparations but before your tests. afterEach()用于清理,而不是在准备之后但在测试之前执行代码。 $scope.$apply() is not cleanup either. $scope.$apply()也不是清理。

You need to be doing the following: 您需要执行以下操作:

// setup async behavior
var all = $q.all(x.promise, y.promise)

// resolve your deferreds/promises
x.reject(); y.reject();

// call $scope.$apply() to 'digest' all the promises
$scope.$apply();

// test the results
return all.should.be.rejected;

You're doing an $apply() AFTER your tests are done, not in between setup and evaluation. 您在测试完成后进行$apply() ,而不是在设置和评估之间。

I've tried to find out why the tests are not passing even though at first glance they should. 我试图找出为什么测试没有通过,即使乍一看他们应该。 Of course I would have to move the $scope.$apply(); 当然我必须移动$scope.$apply(); from afterEach since that is not the place to call as @proloser mentioned. 来自afterEach因为那不是@proloser提到的地方。

Even though I have done that, the tests are still not passing. 即使我已经这样做了,测试仍然没有通过。 I've also opened issues on chai-as-promised and angular to see if I get any input/feedback and I've actually been told that it's most likely not to work. 我还打开了关于chai-as-promiseangular的问题 ,看看我是否得到任何输入/反馈,我实际上被告知它最有可能不起作用。 The reason is probably because of Angular $q 's dependency on the digest phase which is not accounted for in the chai-as-promsied library. 原因可能是因为Angular $q依赖于摘要阶段,这在chai-as-promsied库中没有考虑到。

Therefore I've checked the tests with Q instead of $q and it worked just fine, thus strengthening my hypothesis that the fault was not in the chai-as-promised library. 因此,我用Q而不是$ q检查了测试,它运行得很好,从而加强了我的假设,即错误不在chai-as-promise库中。

I've eventually dropped chai-as-promised and I've rewritten my test using Mocha's done callback instead (even though behind the scenes, chai-as-promised does the same): 我最终放弃了chai-as-promise,我用Mocha的done回调改写了我的测试(即使在幕后,chai-as-promise也是如此):

"use strict";


describe("Promise", function () {

    var $rootScope,
        $scope,
        $q;

    beforeEach(angular.mock.inject(function (_$rootScope_, _$q_) {
        $rootScope = _$rootScope_;
        $q = _$q_;
        $scope = $rootScope.$new();
    }));

    it("should resolve promise and eventually return", function (done) {

        var defer = $q.defer();

        defer
            .promise
            .then(function (value) {
                value.should.eql("incredible, this doesn't work at all");
                done();
            });

        defer.resolve("incredible, this doesn't work at all");

        $scope.$apply();

    });

    it("should resolve promises as expected", function (done) {

        var fst = $q.defer(),
            snd = $q.defer();

        fst
            .promise
            .then(function (value) {
                value.should.eql("phew, this works");
            });

        snd
            .promise
            .then(function (value) {
                value.should.eql("wow, this works as well");
            });

        fst.resolve("phew, this works");
        snd.resolve("wow, this works as well");

        var all = $q.all([
            fst.promise,
            snd.promise
        ]);

        all
            .then(function () {
                done();
            });

        $scope.$apply();

    });

    it("should reject promise and eventually return", function (done) {

        $q
            .reject("no way, this doesn't work either?")
            .catch(function (value) {
                value.should.eql("no way, this doesn't work either?");
                done();
            });

        $scope.$apply();

    });

    it("should reject promises as expected", function (done) {

        var promise = $q.reject("sadly I failed for some stupid reason");

        promise
            ["catch"](function (reason) {
                reason.should.eql("sadly I failed for some stupid reason");
            });

        var all = $q.all([
            promise
        ]);

        all
            .catch(function () {
                done();
            });

        $scope.$apply();

    });

});

The above tests will all pass as expected. 以上测试将按预期通过。 There might be other ways to do it, but I could not figure out how else, so if anyone else does, it would be great to have it posted so others can benefit from it. 可能还有其他方法可以做到这一点,但我无法弄清楚其他方面,所以如果其他人这样做,那么将其发布以便其他人可以从中受益将会很棒。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM