Ответ 1
Я новичок в тестировании Angular, но я настроил plnkr, который тестирует очень похожую настройку на ваш с успешным "вторым" тогда/обещанием вызова
http://plnkr.co/edit/kcgWTsawJ36gFzD3CbcW?p=preview
Нижеприведенные фрагменты кода представляют собой несколько упрощенные версии вышеупомянутого plnkr.
Ключевыми моментами, которые я нашел, являются
-
Я отмечаю, что функция traverseTheStuff не вызывает $http/$httpBackend вообще. Он использует только функции, определенные в $q promises, поэтому тестирование предполагает использование $q и вводит, что
var deferred1 = null; var deferred2 = null; var $q = null; beforeEach(function() { inject(function(_$q_) { $q = _$q_; }); }); beforeEach(function() { deferred1 = $q.defer(); deferred2 = $q.defer(); }
-
Функции, которые будут вызваны асинхронно, будут шпионированы/обрезаны с их возвращаемыми значениями обещания, где обещание создается в самом тесте, поэтому их фактическая реализация не вызывается при тестировании traverseTheStuff
spyOn(MyService,'traverseTheStuff').andCallThrough(); spyOn(MyService,'getRootData').andReturn(deferred1.promise); spyOn(MyService,'getNextLevel').andReturn(deferred2.promise); spyOn(MyService,'aggregateTheStuff');
-
В тесте нет никаких вызовов "then", только для "разрешения" на promises, созданного в тесте, за которым следует $rootScope. $apply(), чтобы на самом деле вызвать "тогда" обратные вызовы в traverseTheStuff, которые мы также можем проверить, называются
beforeEach(function() { spyOn(deferred1.promise, 'then').andCallThrough(); }); beforeEach(function() { deferred1.resolve(testData); $rootScope.$apply(); // Forces $q.promise then callbacks to be called }); it('should call the then function of the promise1', function () { expect(deferred1.promise.then).toHaveBeenCalled(); });
-
Каждое обещание должно быть разрешено /$apply-ed, чтобы вызвать следующую "затем" функцию в цепочке. Так. чтобы получить тест для вызова aggregateTheStuff (или, вернее, его заглушка), второе обещание, возвращенное из заглушки getNextLevel, также должно быть разрешено:
beforeEach(function() { deferred2.resolve(testLevel); $rootScope.$apply(); // Forces $q.promise then callbacks to be called }); it('should call aggregateTheStuff with ' + testLevel, function () { expect(MyService.aggregateTheStuff).toHaveBeenCalledWith(testLevel); });
Проблема со всем вышеизложенным заключается в том, что он предполагает определенное поведение от $q и $rootScope. Я был под пониманием модульных тестов, так как это не должно делать этого предположения, чтобы действительно проверить только один бит кода. Я не разработал, как обойти это, или если я не понимаю.