2014-05-03 11 views
8

Sono abbastanza nuovo per Javascript e sto imparando semplicemente AngularJS, ma ho ottenuto la maggior parte dei miei casi di test per funzionare con alcuni ottimi esempi che ho trovato. Purtroppo non riesco a trovare nulla per aiutarmi a testare il mio caso attuale.Come posso testare un ritorno alla cattura di .catch in AngularJS usando Jasmine?

Sto testando un controller utilizzando un servizio mocked il cui metodo restituisce una promessa. Mi piacerebbe che il Servizio deriso restituisse un errore per poter eseguire il blocco '.catch' nel metodo del controller. Posso dire che non è sempre chiamato correttamente in un paio di modi:

  1. sto usando istanbul per la copertura del codice e mi sta dicendo non mi copre la 'cattura'
  2. Il codice nella ' .catch 'blocco non è sempre eseguita da quello che posso dire tramite il debug

il controller in prova, in particolare bisogno di testare la' .catch' in $ scope.login:

login.js

'use strict'; 

angular.module('ibcwebDashApp') 
    .controller('LoginCtrl', function ($scope, Auth, $location) { 
    $scope.user = {}; 
    $scope.errors = {}; 

    $scope.login = function(form) { 
     $scope.submitted = true; 

     if(form.$valid) { 
     Auth.login({ 
      email: $scope.user.email, 
      password: $scope.user.password 
     }) 
     .then(function() { 
      // Logged in, redirect to home 
      $location.path('/'); 
     }) 
     .catch(function(err) { 
      err = err.data; 
      $scope.errors.other = err.message; 
     }); 
     } 
    }; 
    }); 

Il servizio e il metodo che sto cercando di prendere in giro:

Auth.login

'use strict'; 

angular.module('ibcwebDashApp') 
    .factory('Auth', function Auth($location, $rootScope, Session, User, $cookieStore) { 

    // Get currentUser from cookie 
    $rootScope.currentUser = $cookieStore.get('user') || null; 
    $cookieStore.remove('user'); 

    return { 

     /** 
     * Authenticate user 
     * 
     * @param {Object} user  - login info 
     * @param {Function} callback - optional 
     * @return {Promise}    
     */ 
     login: function(user, callback) { 
     var cb = callback || angular.noop; 

     return Session.save({ 
      email: user.email, 
      password: user.password 
     }, function(user) { 
      $rootScope.currentUser = user; 
      return cb(); 
     }, function(err) { 
      return cb(err); 
     }).$promise; 
     }, 

E, infine, il mio file di prova. La parte divertente è che tutti i test stanno passando ma l''aspettativa' nell'ultimo test può essere cambiata in praticamente qualsiasi cosa e passa ancora. Le prime due prove sembrano funzionare come previsto, ma l'ultimo test è dove sto cercando di eseguire il blocco catch lanciando un errore dal servizio Auth finto:

login.unit.js

'use strict'; 

describe('Controller: LoginCtrl', function() { 
    var $scope, $location, loginCtrl, mockAuthService; 


    beforeEach(function() { 
    mockAuthService = jasmine.createSpyObj('Auth', ['login']); 

    module('ibcwebDashApp'); 

    module(function($provide) { 
     $provide.value('Auth', mockAuthService); 
    }); 

    inject(function($rootScope, $controller, $q, _$location_) { 
     //create an empty scope 
     $scope = $rootScope.$new(); 
     $location = _$location_; 
     //declare the controller and inject our empty scope 
     loginCtrl = $controller('LoginCtrl', {$scope: $scope, Auth: mockAuthService}); 

    }); 

    }); 


    describe('successful login', function() { 

    beforeEach(function() { 
     inject(function($q) { 
     mockAuthService.login.andReturn($q.when()); 
     }); 

    }); 

    it('should call auth.login with the scope email and password when form is valid', function() { 
     //given 
     $scope.form = {}; 
     $scope.form.$valid = true; 
     $scope.user.email = '[email protected]'; 
     $scope.user.password = 'password123'; 

     //when 
     $scope.login($scope.form); 

     //then 
     expect($scope.submitted).toBe(true); 
     expect(mockAuthService.login).toHaveBeenCalledWith({email:'[email protected]', password:'password123'}); 

     $scope.$apply(); //force return of auth.login promise 

     expect($location.path()).toBe('/'); 
    }); 

    it('should not call auth.login if form is invalid', function() { 
     //given 
     $scope.form = {}; 
     $scope.form.$valid = false; 

     //when 
     $scope.login($scope.form); 

     expect(mockAuthService.login).not.toHaveBeenCalled(); 
    }); 
    }); 

    describe('unsuccessful login', function() { 

    beforeEach(function() { 
     inject(function() { 
     mockAuthService.login.andReturn($q.when(new Error('Bad Login!'))); 
     }); 

     it('should set errors.other to the returned auth error message', function() { 
     //given 
     $scope.form = {}; 
     $scope.form.$valid = true; 

     //when 
     $scope.login($scope.form); 

     $scope.$apply(); 

     //then 
     expect($scope.errors.other).toEqual('Bad Login!'); 
     }); 

    }); 
    }); 
}); 

Mi scuso per aver postato tanto codice ma volevo fornire il maggior numero possibile di contesto. Apprezzo davvero tutti coloro che possono darmi una mano mentre imparo il mio modo di testare le unità angolari e le promesse! Grazie!!!

** UPDATE **

sono stato in grado di risolvere il mio problema con qualche aiuto da sotto e scoprire alcuni errori sintattici. Ecco cosa ha risolto questo:

  1. mio beforeEach l'ultimo test non è stato chiuso correttamente e in realtà racchiusa l'ultimo test inducendolo a non funzionare correttamente (o forse a tutti). Questo è il motivo per cui la modifica delle condizioni previste non ha causato errori.
  2. Ho cambiato il mio beforeEach iniettare a: mockAuthService.login.andReturn($q.reject({data: {message: 'Bad Login!'}})); utilizzando il reject suggerito di seguito.
  3. Una volta ho chiuso bene la beforeEach ho ricevuto un messaggio di errore che $ q non è stato definito così ho dovuto aggiunto al inject(function($q)

Una volta ho corretto questi temi, la promessa è stata correttamente respinto e l'errore è stato catturato dalla il codice appropriato nel controller.

risposta

8

prima o durante l'esecuzione di test, deridere fuori parte dell'ambiente in questo modo:

var originalAuthLogin = Auth.login; 
Auth.login = function() { 
    return Promise.reject({data: {message: 'Error message'}}); 
}; 

Dopo il test di ripristinare l'ambiente di sanità mentale:

Auth.login = originalAuthLogin; 

Ciò richiede immediatamente l'.catch() blocco di il codice che stai cercando di testare.

+1

Dan, grazie per l'aiuto! Devo segnare la tua risposta in quanto la soluzione ha anche pensato che non la usassi esattamente come postata? Certamente mi ha indirizzato nella giusta direzione. – sbrown

+1

certo, puoi contrassegnarlo come una soluzione se risolve il tuo problema ... non ha importanza per me in entrambi i casi. –

+0

Nel mio caso non posso usare Promise.reject – svassr

Problemi correlati