我的CompanyService是:

angular.module('mean').service('CompanyService', ['$http', '$rootScope', '$q', function($http, $rootScope, $q) {
  var company = this;
  var initializedDeferred = $q.defer();

  company.company_data = {}
  company.initialized = initializedDeferred.promise;

  company.getCompany = function() {
    return company.company_data;
  }

  company.get = function (company_id) {
    return $http({
      url: '/api/v1/company/' + company_id,
      method: 'GET',
      headers: {
        api_key: $rootScope.api_key
      }
    }).success(function(response) {
      if(response.status === 'ok') {
        company.company_data = response.company;
        initializedDeferred.resolve();
      } else {
        alert('TBD: Need fail case');
      }
    });
  };
}]);

我的 Controller 是:
angular.module('mean').controller('LocationController', ['$scope', '$location', '$rootScope', 'LocationService', 'UserService', 'CompanyService', '$modal', '$routeParams', function ($scope, $location, $rootScope, LocationService, UserService, CompanyService, $modal, $routeParams) {
  $rootScope.menuItem = 'locations';
  $scope.contentTemplate = '/views/location/index.html';
  $scope.locations = [];
  $scope.current_location = null;
  $scope.newLocation = {};
  $scope.location_parent_id = $routeParams.location_parent_id;

  $scope.index = function() {
    CompanyService.initialized.then(function() {
      $scope.test = 'a';
      var company_id = CompanyService.getCompany()._id;
      LocationService.list(company_id, $routeParams.location_parent_id).then(function(response) {
        if(response.data.status === 'ok') {
          $scope.locations = response.data.locations;
          $scope.current_location = response.data.location || null;
        }
      });
    });
  }

  $scope.addLocationModal = function() {
    $scope.location_types = ['warehouse', 'section', 'row', 'shelf', 'bin'];
    $modal({
      scope: $scope,
      template: '/views/location/addLocationModal.html',
      show: true,
      animation: 'am-fade-and-scale'
    });
  }

  $scope.createLocation = function() {
    $scope.newLocation.company_id = CompanyService.getCompany()._id;
    LocationService.create($scope.newLocation).then(function(response) {
      if(response.data.status === 'ok') {
        $scope.$hide();
      } else {
        alert('TBD');
      }
    });
  }

}]);

我的测试是:
(function() {
  describe('LocationController', function() {
    var $scope, $location, $rootScope, $modal, deferred, CompanyService, createController;

    beforeEach(module('mean'));

    beforeEach(inject(function($injector) {
      $location = $injector.get('$location');
      $rootScope = $injector.get('$rootScope');
      $modal = $injector.get('$modal');
      $scope = $rootScope.$new();

      var $controller = $injector.get('$controller');

      var $q = $injector.get('$q');

      var params = {
        '$scope': $scope,
        CompanyService: jasmine.createSpyObj('CompanyService', ['initialized'])
      }

      params.CompanyService.initialized.andCallFake(function () {
        deferred = $q.defer();
        return deferred.promise;
      });


      createController = function() {
        return $controller('LocationController', params);
      };
    }));

    it('should instantiate initial variables at the top level', function() {
      var controller = createController();

      $location.path('/company/locations');
      expect($location.path()).toBe('/company/locations');
      expect($rootScope.menuItem).toBe('locations');
      expect($scope.contentTemplate).toBe('/views/location/index.html');
      expect($scope.locations.length).toEqual(0);
      expect($scope.current_location).toBeNull();
      expect($scope.newLocation).toBeDefined();
      expect($scope.location_parent_id).not.toBeDefined();
    });

    it('should list all locations for this company', function() {
      var controller = createController();
      deferred.resolve();
      $scope.index();

      expect($scope.test).toBe('a');
    });
  });
})();

但是,resolve无法正常工作。我收到此错误:TypeError: 'undefined' is not an object (evaluating 'deferred.resolve')
有什么帮助吗?

最佳答案

在测试中,您使用伪造的CompanyService.initialized函数创建延迟对象。但是,仅当您调用$scope.index();时才调用此函数,该命令在deferred.resolve();行之后执行。以下应该工作:

it('should list all locations for this company', function() {
  var controller = createController();
  $scope.index();   // Should in turn call the fake CompanyService.initialized function that creates deferred
  deferred.resolve();
  $scope.$apply();  // Fire $digest cycle to dispatch promises.

  expect($scope.test).toBe('a');
});

更新

Jasmine 不支持监视不是功能的属性。因此,您的 spy 设置​​无效,因为CompanyService.initialized是对象而不是函数,因此andCallFake无法正常工作。解决方法是在CompanyService中引入getter函数,例如:
company.isInitialized(){ return company.initialized; }

然后在您的 Controller 内部使用此getter函数代替:
$scope.index = function() {
  CompanyService.isInitialized().then(function() {
    $scope.test = 'a';
    // Removed for brevity
  });
}

最后更新您的测试代码初始化:
var params = {
  '$scope': $scope,
  CompanyService: jasmine.createSpyObj('CompanyService', ['isInitialized'])
}
params.CompanyService.isInitialized.andCallFake(function () {
  deferred = $q.defer();
  return deferred.promise;
});

10-06 00:16