Модульный тестовый код на основе обещаний в Angularjs


у меня возникли трудные времена, пытаясь проверить код на основе обещаний в Angularjs.

у меня есть следующий код в мой контроллер:

    $scope.markAsDone = function(taskId) {
        tasksService.removeAndGetNext(taskId).then(function(nextTask) {
            goTo(nextTask);
        })
    };

    function goTo(nextTask) {
        $location.path(...);
    }

Я хотел бы проверить следующие случаи:

  • , когда markAsDone называется он должен позвонить tasksService.removeAndGetNext
  • , когда tasksService.removeAndGetNext делается это должно изменить местоположение (invoke goTo)

мне кажется, что нет простого способа проверить эти два случая отдельно.

что я сделал, чтобы проверить первый был:

var noopPromise= {then: function() {}}
spyOn(tasksService, 'removeAndGetNext').andReturn(noopPromise);

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

есть ли другой способ проверить такие вещи? Или мой дизайн пахнет?

3 67

3 ответа:

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

var $rootScope;

beforeEach(inject(function(_$rootScope_, $q) {
  $rootScope = _$rootScope_;

  var deferred = $q.defer();
  deferred.resolve('somevalue'); //  always resolved, you can do it from your spec

  // jasmine 2.0
  spyOn(tasksService, 'removeAndGetNext').and.returnValue(deferred.promise); 

  // jasmine 1.3
  //spyOn(tasksService, 'removeAndGetNext').andReturn(deferred.promise); 

}));

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

Of конечно, вы бы сохранили свои тесты, как они есть, но вот некоторые действительно простые спецификации, чтобы показать вам, как это будет работать.

it ('should test receive the fulfilled promise', function() {
  var result;

  tasksService.removeAndGetNext().then(function(returnFromPromise) {
    result = returnFromPromise;
  });

  $rootScope.$apply(); // promises are resolved/dispatched only on next $digest cycle
  expect(result).toBe('somevalue');
});

другой подход будет следующим, взятым прямо из контроллера, который я тестировал:

var create_mock_promise_resolves = function (data) {
    return { then: function (resolve) { return resolve(data); };
};

var create_mock_promise_rejects = function (data) {
    return { then: function (resolve, reject) { if (typeof reject === 'function') { return resolve(data); } };
};

var create_noop_promise = function (data) {
    return { then: function () { return this; } };
};

и в качестве еще одного варианта вы можете избежать необходимости звонить $digestС помощью библиотеки Q (https://github.com/kriskowal/q) в качестве замены для$q например:

beforeEach(function () {
    module('Module', function ($provide) {
        $provide.value('$q', Q); 
    });
});

таким образом, обещания могут быть разрешены / отклонены за пределами цикла $ digest.