Модульный тест AngularJS для директивы, которая запрашивает сервер, возвращает неудовлетворенные запросы

У меня есть приложение для закладок, которое принимает URL-адрес и автоматически извлекает сводку. Когда клиент запрашивает у сервера добавление новой закладки, сервер возвращает некоторую исходную информацию и инициирует процесс извлечения сводки.

В интерфейсе angular я создал директивы для добавления закладки и управления каждым элементом в списке закладок. В директиве listitem есть метод checkSummary(), который будет опрашивать сервер для получения сводки.

У меня проблемы с модульным тестом для этой последней директивы. Он терпит неудачу с «Неудовлетворенным запросом», но когда я вхожу в консоль в разных точках, кажется, что запрос $http.get() срабатывает, и я вижу, что переменные $scope обновляются, поэтому я действительно не понимаю, почему это может привести к сбою по этой причине. Я проверил ответы на множество разных вопросов, но не могу найти ничего, что могло бы дать некоторое представление.

Код следующий:

bookmarks.js:

angular.module( 'userpages.bookmarks', [
  'ui.router',
  'ui.bootstrap',
  'ui.validate'
])

.config(function config( $stateProvider ) {
  $stateProvider.state( 'userpages.bookmarks', {
    url: '/bookmarks',
    templateUrl: 'userpages/bookmarks/bookmarks.tpl.html',
    controller: 'BookmarksController',
    data: { pageTitle: 'Bookmarks' },
  });
})

.factory('bookmarksApiResource', ['$http', function ($http) {
  var bookmarksUrl = '/api/v1/bookmarks/';
  var api = {
    getList: function() {
      return $http.get( bookmarksUrl )
              .then( function(response) { return response.data; });
    },
    getById: function(id) {
      return $http.get( bookmarksUrl + id + '/' )
              .then( function(response) { return response.data; });
    },
    addBookmark: function(articleUrl) {
      return $http.post( bookmarksUrl, {article_url: articleUrl})
              .then( function(response) { return response.data; });
    },
    checkSummary: function(id) {
      return $http.get( bookmarksUrl + id + '/?fields=summary' )
              .then( function(response) { return response.data; });
    }
  };
  return api;
}])

.controller( 'BookmarksController', ['$scope', '$stateParams', 'bookmarksApiResource',
   function BookmarksController( $scope, $stateParams, bookmarksApiResource) {

    $scope.username = $stateParams.username; 
    $scope.templates = {
      bookmarks: {
        toolbar: 'userpages/bookmarks/bookmarks-toolbar.tpl.html',
        listitem: 'userpages/bookmarks/bookmarks-listitem.tpl.html',
      }
    };
    $scope.addBookmarkFormCollapsed = true;

    $scope.bookmarks = [];
    bookmarksApiResource.getList().then( function(data) {
      $scope.bookmarks = data;
    });
}])

.directive('newBookmark', ['bookmarksApiResource', function(bookmarksApiResource) {
  var newBookmark = {
    restrict: 'E',
    templateUrl: 'userpages/bookmarks/bookmarks-add-form.tpl.html',
    replace: true,
    link: function($scope, $element, $attrs, $controller) {
      $scope.addBookmark = function(articleUrl) {
        var newBookmark = bookmarksApiResource.addBookmark(articleUrl);
        $scope.bookmarks.push(newBookmark);
      };
    }
  };
  return newBookmark;
}])

.directive('bookmarksListitem', ['bookmarksApiResource', '$timeout',
                                 function(bookmarksApiResource, $timeout) {
  var listitem = {
    restrict: 'E',
    templateUrl: 'userpages/bookmarks/bookmarks-listitem.tpl.html',
    replace: true,
    scope: true,
    link: function($scope, $element, $attrs, $controller) {

      var checkSummary = function() {
        if (!$scope.bookmark.summary_extraction_done) {
          bookmarksApiResource.checkSummary($scope.bookmark.id).then(function(data){
            if (data.summary_extraction_done) {
              $scope.bookmark.summary_extraction_done = data.summary_extraction_done;
              $scope.bookmark.summary = data.summary;
            } else {
              $timeout(checkSummary, 1000);
            }
          });
      }

      checkSummary();
    }
  };
  return listitem;
}])
;

а тест такой:

bookmarks.spec.js

describe( 'userpages.bookmarks', function() {

  var $rootScope, $location, $compile, $controller, $httpBackend;
  var $scope, elem;

  beforeEach( module( 'userpages.bookmarks', 'ui.router' ) );

  ... other tests ...

  describe('bookmarks-listitem', function() {
    var testBookmark; 

    beforeEach(module('userpages/bookmarks/bookmarks-listitem.tpl.html'));
    beforeEach(inject(function(_$rootScope_, _$compile_, _$httpBackend_){
      $rootScope = _$rootScope_;
      $compile = _$compile_;
      $httpBackend = _$httpBackend_;

      testBookmark = {
        id: 1,
        title: 'Title of our first article',
        url: 'http://www.example.com/some/article',
        summary_extraction_done: false,
        summary: '',
      };

    }));

    it('when summary_extraction_done = false, checkSummary() should call the server and update the summary with the response', function () {
      $httpBackend.when('GET', '/api/v1/bookmarks/1/?fields=summary').respond(200, {
        summary_extraction_done: true,
        summary: 'This is the summary for article 1.'
      });
      $httpBackend.expect('GET', '/api/v1/bookmarks/1/?field=summary');

      $scope = $rootScope.$new();
      $scope.bookmark = testBookmark;
      elem = $compile('<bookmarks-listitem></bookmarks-listitem>')($scope);
      $scope.$digest();
      $httpBackend.flush();

      expect($scope.bookmark.summary_extraction_done).toBe(true);
      expect($scope.bookmark.summary).toBe('This is the summary for article 1.');
    });

  });
});

И результаты теста таковы:

Firefox 27.0.0 (Mac OS X 10.9) userpages.bookmarks bookmarks-listitem when summary_extraction_done = false, checkSummary() should call the server and update the summary with the respone FAILED
    Error: Unsatisfied requests: GET /api/v1/bookmarks/1/?field=summary in .../frontend/vendor/angular-mocks/angular-mocks.js (line 1486)
    createHttpBackendMock/$httpBackend.verifyNoOutstandingExpectation@.../frontend/vendor/angular-mocks/angular-mocks.js:1486
    createHttpBackendMock/$httpBackend.flush@.../frontend/vendor/angular-mocks/angular-mocks.js:1464
    @.../frontend/src/app/userpages/bookmarks/bookmarks.spec.js:6

Любые предложения были бы полезны.


person nsecord    schedule 10.03.2014    source источник


Ответы (2)


Опечатка здесь:

$httpBackend.expect('GET', '/api/v1/bookmarks/1/?field=summary');

Вы пропустили s в конце поля.

person Ed_    schedule 10.03.2014
comment
Ах, чувак, таскать меня за волосы в течение 2 дней из-за глупой опечатки, это смущает. Спасибо! - person nsecord; 10.03.2014
comment
Худший вид "бага" :( - person Ed_; 11.03.2014

Если вы хотите увидеть, какой URL-адрес использует httpBackend, вы можете использовать следующий код для его отладки.

$httpBackend.when("GET", new RegExp('.*')).respond(function(method, url, data) {
   console.log("URL:",url);
});
person Koray Güclü    schedule 30.09.2015