модульное тестирование angularjs $q.all - обещание никогда не завершается

Я пытаюсь протестировать созданный мной сервис, использующий $q Angular. выполнение Обещаний. Я использую комбинацию Karma, Mocha, Chai, Sinon, Sinon Chai и Chai as Promised.

Все тесты, которые я написал и возвращают обещания, проходят, кроме тех, которые отклоняют или используют $q.all([ ... ]). Я перепробовал все, что мог придумать, но не могу найти, в чем проблема.

Ниже приведена тонкая версия того, что я тестирую:

"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;
    });

});

3-й, последний и первый тест - те, которые не пройдены. На самом деле это не сбой, он просто разрешается после превышения тайм-аута, и я получаю Error: timeout of 2000ms exceeded.

EDIT: я только что попытался протестировать реализацию Kris Kowal обещания, и это прекрасно работает с этим.

P.S. На самом деле я обнаружил, что какое-то время было потрачено где-то в мисках Mocha, Chai или Chai As Promised, а хук afterEach вызывается позже тайм-аута.


person Roland    schedule 05.11.2014    source источник
comment
Если я понимаю псевдокод, похоже, вы не звоните $scope.$apply()до тех пор, пока не отправите сообщение об ожиданиях. Можете ли вы попробовать вызвать его сразу после того, как вы разрешите/отклоните обещание?   -  person Michal Charemza    schedule 08.11.2014
comment
На самом деле я тоже пробовал это, скоро у меня будет скрипка с этими тестами.   -  person Roland    schedule 08.11.2014
comment
Запуск $scope.$apply() в afterEach может быть проблемой, если вы ожидаете значение промисов до запуска afterEach   -  person Khanh TO    schedule 09.11.2014
comment
@khanhto - это правда, и @proloser упомянул, что я использую afterEach только для очистки. Это просто не пришло мне в голову в тот момент, когда я это сделал.   -  person Roland    schedule 10.11.2014


Ответы (2)


afterEach() используется для очистки, а не для выполнения кода после ваших приготовлений, а перед вашими тестами. $scope.$apply() тоже не чистка.

Вам нужно делать следующее:

// 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;

Вы выполняете $apply() ПОСЛЕ завершения тестов, а не между настройкой и оценкой.

person ProLoser    schedule 09.11.2014
comment
Если хотите, можете попробовать протестировать его на этом plnk, который у меня есть тяжело его настроить. Я думаю, что один из моих ресурсов недействителен или что-то в этом роде, потому что я получаю сообщение об ошибке в консоли, которое сообщает, что мне не хватает beforeEach. Я никогда не пытался тестировать в браузере, поэтому буду признателен за помощь в его работе, чтобы мы могли увидеть, действительно ли этот ответ работает, и у нас также будет онлайн-ссылка, чтобы другие пользователи могли его увидеть. - person Roland; 10.11.2014
comment
Это то, что я написал в комментарии, что была бы признательна за помощь в настройке его в браузере, поскольку я никогда не делал этого таким образом. И вы можете просто сделать plnkr и применить свое исправление и показать, что оно действительно работает, если вы, конечно, хотите. Делая это локально на моей машине и говоря, что это работает, это не поможет другим пользователям, столкнувшимся с той же проблемой. - person Roland; 11.11.2014
comment
ваше предложение не работает, и я уже пробовал то, что вы предлагали раньше. Вот почему я спросил, можете ли вы помочь настроить тесты в браузере, чтобы вы могли проверить его и других пользователей, если бы это сработало. - person Roland; 14.11.2014

Я попытался выяснить, почему тесты не проходят, хотя на первый взгляд должны. Конечно, мне пришлось бы переместить $scope.$apply(); из afterEach, поскольку это не то место, где можно звонить, как упоминал @proloser.

Несмотря на то, что я сделал это, тесты все еще не проходят. Я также открыл вопросы по chai-as-promised и angular, чтобы увидеть, получаю ли я какой-либо ввод/отзыв, и действительно ли я сказали, что скорее всего не получится. Причина, вероятно, связана с зависимостью Angular $q от фазы дайджеста, которая не учитывается в библиотеке chai-as-promsied.

Поэтому я проверил тесты с помощью Q вместо $q, и это сработало просто отлично, тем самым укрепив мою гипотезу о том, что ошибка была не в библиотеке chai-as-promise.

В конце концов я отказался от chai-as-promised и переписал свой тест, используя вместо этого обратный вызов done Mocha (хотя за кулисами chai-as-promised делает то же самое):

"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();

    });

});

Все вышеперечисленные тесты пройдут, как и ожидалось. Могут быть другие способы сделать это, но я не мог понять, как еще, поэтому, если кто-то еще это сделает, было бы здорово опубликовать его, чтобы другие могли извлечь из него пользу.

person Roland    schedule 14.11.2014
comment
@blacksonic - нет проблем, я потратил несколько дней, пока не нашел исправление :) - person Roland; 23.02.2015