how to return rejected/failure promise to through jasmine spy to an angular controller in a unit test

15,989

I find it easier to use actual promises for mocked services as it removes a lot of nested functions and is a lot easier to read.

Relevant snippets ($q needs to be injected in beforeEach):

deferred = $q.defer();
spyOn(login, 'login').andReturn(deferred.promise);

...

deferred.reject({ ... });

After resolving or rejecting the promise you need to call scope.$digest() so angular can process it.

Share:
15,989
Nishutosh Sharma
Author by

Nishutosh Sharma

Updated on July 29, 2022

Comments

  • Nishutosh Sharma
    Nishutosh Sharma almost 2 years

    I am using jasmine for testing my angular controllers. I am catching errors and success in the .then(successCallback, errorCallback) Although it is working fine on the bases of live functionality but am confused how to write a spy for returning an error as it is always caught in the successCallback()

    Following is the controller :-

    angular.module('myApp')
    .controller('LoginCtrl', function ($scope, $location, loginService, SessionService) {
        $scope.errorMessage = '';
    
        $scope.login = function () {
    
          var credentials = {
              email: this.email,
              password: this.password
          };
    
         SessionService.resetSession();
         var request = loginService.login(credentials);
    
         request.then(function(promise){ //successfull callback
            if (promise['status'] === 200){
                 //console.log('login');
                $location.path('/afterloginpath');
            }
          },
         function(errors){  //fail call back
          //  console.log(errors);
            $location.path('/login');
         });
        };
    });
    

    My test case :-

    'use strict';
    
    describe('Controller: LoginCtrl', function () {
    
      // load the controller's module
      beforeEach(module('myApp'));
    
      var LoginCtrl, scope, location, login, loginReturn, session;
      var credentials = {'email': '[email protected]', 'password': 'admin123'};
    
      // Initialize the controller and a mock scope
     beforeEach(inject(function ($controller, $rootScope, $location, _loginService_, _SessionService_) {
    
        scope = $rootScope.$new();
        LoginCtrl = $controller('LoginCtrl', {
          $scope: scope
        });
    
        location = $location;
        login = _loginService_;
        session = _SessionService_;
        scope.errorMessage = '';
        spyOn(login, "login").andCallFake(
          function(){
            return {
              then: function(response){
                response(loginReturn);
              }
            }
          }
        );
    
        spyOn(session, "setName").andCallFake(function(){
          return true;
        });
      }));
    
      it('should go to login when login fail', function () {
        loginReturn = function(){
          return{
            successfullyCallback: {throwError:true},
            failCallback: {status: 400, 'data' : {'errors' : [{"type":"invalid_data","target":"email,password"}]}}
          }
        };
    
        var wrong_creds = {email: '[email protected]', password: 'wrong_password'};
        scope.email = wrong_creds.email;
        scope.password = wrong_creds.password;
    
    scope.login();
    
        expect(location.path()).toBe("/login");
        expect(scope.errorMessage).toBe('username or password combination is invalid');
      });
    
    });
    
  • Nishutosh Sharma
    Nishutosh Sharma almost 10 years
    It works, we can also call scope.$apply() is also works for me
  • ivarni
    ivarni almost 10 years
    scope.$apply() works because it triggers scope.$digest() but $apply() is meant to be called with an expression so I tend to prefer using $digest() instead. Take a look at docs.angularjs.org/api/ng/type/$rootScope.Scope#$apply for the difference between them.
  • Muhammad Hashir Anwaar
    Muhammad Hashir Anwaar over 9 years
    Jasmine 2.0 syntax is: and.returnValue();