Angularjs Mocha тестирует обещания $q без $rootScope.$apply

У меня есть эта услуга:

    angular.module('domeeApp')
        .factory('streamWidget', streamWidgetFactory);

    function streamWidgetFactory($q) {
        return {
            loadContent: function() { 
                return $q(function(resolve, reject) {
                    resolve('test');
                }) 
            }
        }
    }

Я тестирую это с помощью karma/mocha/chai:

describe('streamWidget', function() {
    beforeEach(module('domeeApp'));
    var streamWidget;
    var $timeout;

    beforeEach(inject(function(_$timeout_, _streamWidget_) {
        streamWidget = _streamWidget_;
        $timeout = _$timeout_;
    }));


    it('should load new content', function(done) {        
        streamWidget.loadContent()
        .then(function(res) {
            expect(res).to.equal('test');
            done();
        })
        .catch(function(){})
        $timeout.flush();
    });    
});

Поскольку обещания $q плохо работают с мокко, я следую этому ответу, в котором говорится, что нужно добавить $timeout.flush(), чтобы заставить .then метод выполнения обещания.

Проблема в том, что после вызова .flush() все мое приложение просыпается, и я начинаю получать эти ошибки от angular-mocks:

Error: Unexpected request: GET /partials/page/view/index.

Я знаю о $httpBackend, но было бы безумно имитировать ВСЕ запросы, которые мое приложение делает при запуске.

Есть ли способ заставить $q promises работать с мокко без вызова $timeout.flush() или $rootScope.$apply()?


person pietrovismara    schedule 29.07.2016    source источник
comment
Предмет вопроса - чай, а не мокко. Возможный дубликат stackoverflow.com/a/37374041/3731501   -  person Estus Flask    schedule 29.07.2016
comment
Предметом вопроса является служба $ q и как я могу заставить обещания $ q быть разрешены без использования apply/flush. Чай тут ни при чем, так как обещанный чай я не использую. По крайней мере, я так вижу ситуацию, если вы не согласны, попытайтесь объяснить это лучше.   -  person pietrovismara    schedule 29.07.2016
comment
Утверждения делает Чай, а не Мокко. Что ж, тогда вы можете рассмотреть возможность использования chai-as-promised, это способ, которым можно выполнять утверждения $q promise без вызова $apply() или $digest() после then.   -  person Estus Flask    schedule 29.07.2016
comment
Пробовал с чай-как-обещано, но с тем же результатом. Если вы можете показать мне рабочее решение, я приму ваш ответ.   -  person pietrovismara    schedule 29.07.2016
comment
Да, chai-as-promised требует дополнительной настройки, как следует из приведенной выше ссылки. К счастью, у него есть хук transferPromiseness, обеспечивающий желаемое поведение для утвержденного промиса (т. е. запуск дайджеста для выполнения всей цепочки промисов).   -  person Estus Flask    schedule 29.07.2016


Ответы (2)


Как показано здесь, chai-as-promised можно использовать для утверждения $q обещаний. С этой настройкой

chaiAsPromised.transferPromiseness = function (assertion, promise) {
  assertion.then = promise.then.bind(promise);

  if (!('$$state' in promise))
    return;

  inject(function ($rootScope) {
    if (!$rootScope.$$phase)
      $rootScope.$digest();
  });
};

Циклы дайджеста будут запускаться автоматически для утверждений промисов, выполняя всю цепочку промисов.

В этом случае спец.

it('...', () => {
  ...
  expect(...).to.eventually...;
  expect(...).to.eventually...;
  $rootScope.$digest();
});

может опустить $digest() вызов и стать

it('...', () => {
  ...
  expect(...).to.eventually...;
  expect(...).to.eventually...;
});

Обратите внимание, что обещания $q являются синхронными, они не должны возвращаться из спецификации Mocha или вызывать обратный вызов done.

person Estus Flask    schedule 29.07.2016
comment
Я попробовал ваше решение, но результат тот же. Когда я звоню $rootScope.$digest(), будь то в chaiAsPromised.transferPromiseness или где-то еще, я начинаю получать те же ошибки о неожиданных запросах. На самом деле, с самого начала я спрашиваю, как выполнить этот тест БЕЗ использования $digest/$apply/$flush. На данный момент я думаю, что нет решения. - person pietrovismara; 01.08.2016
comment
Кажется, вы смешиваете вещи. Это все отдельные заботы. $digest() не панацея. Вам НЕОБХОДИМО запустить $digest() для выполнения обещаний $ q (это то, что касается ответа). Вам НЕОБХОДИМО вызвать flush() для выполнения тайм-аутов $timeout. Вам НУЖНО имитировать запросы $httpBackend в модульных тестах. Нет необходимости издеваться над ними всеми - только те запросы, которые делает тестируемый модуль. Вот как работает тестирование Angular. - person Estus Flask; 01.08.2016

Вот альтернативная стратегия, которую мы используем, потому что нам никогда на самом деле не нужен $httpBackend, но иногда (случайно) не удается выполнить запросы шаблонов, используемых директивами (даже если эти шаблоны доступны в $templateCache):

beforeEach(function() {
  module('app', function($provide) {
    // This is using jasmine, but the idea is the same with mocha.
    // Basically just replace $httpBackend with a function that does nothing.
    $provide.constant('$httpBackend', jasmine.createSpy('$httpBackend'));
  });
});

Конечно, если вы на самом деле используете $httpBackend в других случаях, то это не сработает, так как вам понадобится имитировать объекты ответа.

person tandrewnichols    schedule 01.04.2017