My CompanyService
is:
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');
}
});
};
}]);
My controller is:
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');
}
});
}
}]);
My test is:
(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');
});
});
})();
But, the resolve
isn't working. I get this error:
TypeError: 'undefined' is not an object (evaluating 'deferred.resolve')
Any help?
Introduction. Mocking is a great idea for testing Angular apps because it makes maintenance easier and helps reduce future bugs. There are a few complex tools, such as XUnit, for mocking an Angular CLI project. You can execute the mocking methods described in this guide only if you use vanilla Jasmine + Angular Testbed ...
Promise is like giving some work to someone and he promises you that the work will be complete. Now until the work is done you can prepare yourself for three situations i.e. he'll do it in a perfect manner, he'll do it but it will not be useful for you, and lastly he has not done it due to some reason.
In your tests you create your deferred object using the fake CompanyService.initialized
function. However this function is only called when you call $scope.index();
, which is executed after your deferred.resolve();
line. The following should work:
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');
});
Update
Jasmine doesn't support spying on properties that are not functions. So your spy setup is invalid as CompanyService.initialized
is an object and not a function, so your andCallFake
wont work. A workaround is to introduce a getter function inside your CompanyService
e.g:
company.isInitialized(){ return company.initialized; }
And then inside your controller use this getter function instead:
$scope.index = function() {
CompanyService.isInitialized().then(function() {
$scope.test = 'a';
// Removed for brevity
});
}
And finally update your test code initialization:
var params = {
'$scope': $scope,
CompanyService: jasmine.createSpyObj('CompanyService', ['isInitialized'])
}
params.CompanyService.isInitialized.andCallFake(function () {
deferred = $q.defer();
return deferred.promise;
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With