Разрешите вложенные обещания AngularJS в модульном тесте Karma

Я пытаюсь протестировать функцию, которая использует вложенные обещания (давайте предположим, что эту функцию нельзя изменить). Чтобы обработать эти обещания, я должен позвонить $rootScope.$digest() по крайней мере, два раза. Я пришел с этим рабочим решением $digest() каждые 10 мс.

// Set some async data to scope.key
// Note: This function is just for demonstration purposes.
//       The real code makes async calls to localForage.
function serviceCall(scope, key) {
  var d1 = $q.defer(), d2 = $q.defer();

  setTimeout(function () {
    d1.resolve('d1');
  });

  d1.promise.then(function (data) {
    setTimeout(function () {
      d2.resolve(data + ' - d2');
    }, 100); // simulate longer async call
  });

  d2.promise.then(function (data) {
    scope[key] = data;
  });
}

it('nested promises service', function (done) {
  var interval = setInterval(function () {
      $rootScope.$digest();
    }, 10),
    myKey = 'myKey',
    scope = {};

  serviceCall(scope, myKey);

  setTimeout(function () {
    expect(scope[myKey]).toEqual('d1 - d2');
    window.clearInterval(interval);
    done();
  }, 120); // What is the right timeout?
});

Проблема состоит в том, чтобы оценить продолжительность тайм-аута, необходимую для того, чтобы переварить достаточно времени для выполнения всех обещаний. Это усложняется, если служба выполняет настоящий асинхронный вызов, например, localalstorage.

Есть ли другой способ решить эту проблему? Есть ли способ получить все оставшиеся обещания?

1 ответ

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

Обратите внимание, что этот пример не идеален, но сокращен для ясности.

var service = jasmine.createSpyObj('service', ['call']);
var defServiceCall;
var $scope;

beforeEach(function () {
  module('myModule', function ($provide) {
    // NOTE: Replaces the service definition with mock.
    $provide.value('service', service);
  });
  inject(function($q, $rootScope) {
    $scope = $rootScope.$new();
    defServiceCall = $q.defer();
    service.call.and.returnValue(defServiceCall.promise);
  });
});
describe('When resolved', function () {
  beforeEach(function () {
    myFunctionCall(); // Sets myVar on the scope
    defServiceCall.resolve('data');
    $scope.$digest(); // NOTE: Must digest for promise to update
  });
  it('should do something awesome when resolved', function () {
    expect($scope.myVar).toEqual('data');
  });
});
Другие вопросы по тегам