AngularJS error: Unexpected request (no more expectations expected)

In my AngularJS application, it’s hard for me to understand, like a unit test, that keeping the promise then will change location.url. I have a login function that calls a service, AuthenticationService . It returns a promise, and the fulfillment of the then promise changes location.url.

This is the AuthCtrl controller:

angular .module('bloc1App') .controller('AuthCtrl', ['$scope', '$http','$location', 'AuthenticationService', function($scope, $http, $location, AuthenticationService) { this.AuthenticationService = AuthenticationService; var self = this; $scope.login = function(username, password){ self.AuthenticationService .login(username, password) .then(function(){ $location.url('/tasks'); }); }; } ]); 

This is my attempt to use unit test:

  describe('Controller: AuthCtrl', function () { var scope, location, route, q, rootScope, AuthCtrl; beforeEach(module('bloc1App')); beforeEach(inject(function($controller, $rootScope, $location, $route, $q) { scope = $rootScope.$new(); q = $q; rootScope = $rootScope; location = $location; route = $route; AuthCtrl = $controller('AuthCtrl', { $scope: scope, $route: route, $location: location }); })); it('login should redirect user to tasks view', function(){ var deferred = q.defer(); spyOn(AuthCtrl.AuthenticationService, 'login').andReturn(deferred.promise); spyOn(location, 'url'); scope.login('foo', 'foo'); deferred.resolve(); scope.$apply(); expect(location.url).toHaveBeenCalledWith('/tasks'); }); }); 

When I run this test, I get this error. I get the same error if I use rootScope. $ Apply () instead:

 Error: Unexpected request: GET views/AuthView.html No more request expected 

When I retrieve the scope. $ apply (), I get this error:

 Expected spy url to have been called with [ '/tasks' ] but it was never called. 

Any ideas on how to fix this? This is the first time that I am checking a promise, and I probably misunderstand how to do this.

Thank you in advance for your help.

+7
angularjs unit-testing jasmine
source share
2 answers

When you do unit testing with AngularJS, you are actually using mgock services from ngMock instead of real services. In this case, your error probably occurs in the form of $httpBackend mock, which should be loaded first if you expect the units under test to use $http to send real requests. (I do not see your code using $http anywhere, so I assume that you are not showing there.)

Basically, you need to tell the $httpBackend layout that you are expecting a specific request using $httpBackend.when (or you can use $httpBackend.expect if you want the layout to check if the request was actually called) and which response should be returned .

So, your error has nothing to do with promises and everything related to testing the modules that connect to the server.

+4
source share

It may be a moot point to answer the question almost two years after his request, but the accepted answer is not quite right. The problem is that you are not kidding the AuthenticationService . You should completely mock the functionality of the service, only checking that the service was called from the controller, and save the $httpBackend to test the service itself. What you want to do is to introduce a fake AuthenticationService (as well as mock instances of Angular services like $q , $location and $route if we follow best practices) in beforeEach , and then declare it in the definition of $controller . You can also include your spies in the beforeEach block.

 describe('AuthCtrl', function(){ var $q; var $location; var $controller; var AuthenticationService; var $route; var deferred; beforeEach(inject(function( _$controller_, $rootScope, _AuthenticationService_, _$q_, _$location_, _$route_){ scope = $rootScope.$new(); AuthenticationService = _AuthenticationService_; $location = _$location_; $route = _$route_; $controller = _$controller_; $q = _$q_; deferred = $q.defer(); spyOn(AuthenticationService, 'login').andReturnValue(deferred.promise); spyOn($location, 'url'); AuthCtrl = $controller('AuthCtrl', { scope: scope, AuthenticationService: AuthenticationService, $route: $route, $location: $location }; })); it('should redirect the user on login', function(){ deferred.resolve(); scope.$apply(); expect($location.url).toHaveBeenCalledWith('/tasks'); }); }); 

Please note that it is important that your controller defines the last element in the beforeEach block if you define your variables this way, otherwise the layouts will not be properly created and the service methods will actually be called regardless of spyware.

+1
source share

All Articles