Comment je me moque du résultat d'une promesse dans un test d'unité AngularJS?

My CompanyService est:

 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'); } }); }; }]); 

Mon contrôleur est:

 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'); } }); } }]); 

Mon test est:

 (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'); }); }); })(); 

Mais, la resolve ne fonctionne pas. Je reçois cette erreur: TypeError: 'undefined' is not an object (evaluating 'deferred.resolve')

De l'aide?

Dans vos tests, vous créez votre objet différé à l'aide de la fausse fonction CompanyService.initialized . Cependant, cette fonction n'est appelée que lorsque vous appelez $scope.index(); , Qui est exécuté après votre deferred.resolve(); ligne. Ce qui suit devrait fonctionner:

 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'); }); 

Mettre à jour

Jasmine ne supporte pas l'espionnage de propriétés qui ne sont pas des fonctions. Ainsi, votre configuration d'espion n'est pas valide car CompanyService.initialized est un objet et non une fonction, de sorte que votre andCallFake ne fonctionnera pas. Une solution de contournement consiste à introduire une fonction getter dans votre service CompanyService par exemple:

 company.isInitialized(){ return company.initialized; } 

Et à l'intérieur de votre contrôleur, utilisez cette fonction getter à la place:

 $scope.index = function() { CompanyService.isInitialized().then(function() { $scope.test = 'a'; // Removed for brevity }); } 

Et finalement, mettez à jour l'initialisation de votre code de test:

 var params = { '$scope': $scope, CompanyService: jasmine.createSpyObj('CompanyService', ['isInitialized']) } params.CompanyService.isInitialized.andCallFake(function () { deferred = $q.defer(); return deferred.promise; });