Sunday, May 19, 2024
 Popular · Latest · Hot · Upcoming
71
rated 0 times [  76] [ 5]  / answers: 1 / hits: 11644  / 10 Years ago, sat, may 3, 2014, 12:00:00

I'm fairly new to Javascript and just learning AngularJS but I've gotten most of my test cases to work with some great examples I've found. Unfortunately I can't seem to find anything to help me test my current case.



I'm testing a Controller using a mocked Service whose method returns a promise. I would like the mocked Service to return an error in order to execute the '.catch' block in the controller method. I can tell that it's not getting called correctly in a couple of ways:




  1. I'm using istanbul for code coverage and it's telling me I'm not covering the 'catch'

  2. The code in the '.catch' block is not getting executed from what I can tell via debugging



The controller under test, specifically need to test the '.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;
});
}
};
});


The service and method I'm attempting to mock:



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;
},


And finally, my test file. The funny part is that all tests are passing but the 'expect' in the last test can be changed to pretty much anything and it still passes. The first two tests seem to run as expected but the last test is where I'm trying to execute the catch block by throwing an error from the mock Auth service:



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

});
});
});


I apologize for posting so much code but I wanted to provide as much context as possible. I really appreciate anyone who can help me out as I learn my way around unit testing Angular and promises! Thanks!!!



**UPDATE**



I was able to solve my issue with some help from below and discovering some syntactic errors. Here's what fixed this:




  1. My beforeEach on the last test was not closed properly and actually enclosed the last test causing it not to run correctly (or maybe at all). This is why changing the expect conditions resulted in no errors.

  2. I changed my beforeEach inject to: mockAuthService.login.andReturn($q.reject({data: {message: 'Bad Login!'}})); using the reject suggested below.

  3. Once I properly closed the beforeEach I got an error message that $q was not defined so I had to added it to inject(function($q)



Once I corrected these issues the promise was correctly rejected and the error was caught by the appropriate code in the controller.


More From » angularjs

 Answers
2

Before or while running your test, mock out part of the environment like this:



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


After the test restore the environment to sanity:



Auth.login = originalAuthLogin;


This immediately calls the .catch() block of the code you're trying to test.


[#45584] Friday, May 2, 2014, 10 Years  [reply] [flag answer]
Only authorized users can answer the question. Please sign in first, or register a free account.
yosefleod

Total Points: 113
Total Questions: 100
Total Answers: 115

Location: Egypt
Member since Tue, May 3, 2022
2 Years ago
;