Модульный тестовый код на основе обещаний в Angularjs
у меня возникли трудные времена, пытаясь проверить код на основе обещаний в Angularjs.
у меня есть следующий код в мой контроллер:
$scope.markAsDone = function(taskId) {
tasksService.removeAndGetNext(taskId).then(function(nextTask) {
goTo(nextTask);
})
};
function goTo(nextTask) {
$location.path(...);
}
Я хотел бы проверить следующие случаи:
- , когда
markAsDone
называется он должен позвонитьtasksService.removeAndGetNext
- , когда
tasksService.removeAndGetNext
делается это должно изменить местоположение (invokegoTo
)
мне кажется, что нет простого способа проверить эти два случая отдельно.
что я сделал, чтобы проверить первый был:
var noopPromise= {then: function() {}}
spyOn(tasksService, 'removeAndGetNext').andReturn(noopPromise);
теперь, чтобы проверить второй случай, мне нужно создать еще одно поддельное обещание, которое будет всегда resolved
. Это все довольно утомительно, и это много шаблонного кода.
есть ли другой способ проверить такие вещи? Или мой дизайн пахнет?
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.