Reputation: 1508
I use Karma + Jasmine to write Unit Test for an angular project.
I am trying to test an Angular service that returns the correct result via a promise. Below are what I have so far.
Project Service
'use strict';
var app = app || {};
app.factory('ProjectSvc', ['$http', '$q', function($http, $q) {
var project_url = 'https://dl.dropboxusercontent.com/u/2122820/hosted_json/project.json';
var svc = {};
svc.cachedProjects = [];
svc.getProjects = function() {
return $http.get(project_url);
}
function lookUp(slug) {
for (var i=0; i < svc.cachedProjects.length; i++){
if (svc.cachedProjects[i].slug && svc.cachedProjects[i].slug == slug) {
return svc.cachedProjects[i];
} else if (svc.cachedProjects[i].title == slug) {
return svc.cachedProjects[i];
}
}
return null;
}
//return a promise
svc.retrieveProjectById = function(id) {
var deferred = $q.defer();
if (svc.cachedProjects.length > 0 ) {
var found = lookUp(id);
if (!found) {
deferred.reject ({error: 'not found', data: null});
} else {
deferred.resolve(found);
}
} else {
init().then(function(data){
//console.log('---------');
svc.cachedProjects = data.data.projects;
var found = lookUp(id);
//console.log('found: ', found);
if (!found) {
deferred.reject ({error: 'not found', data: null});
} else {
deferred.resolve(found);
}
})
}
return deferred.promise;
}
//return a promise
svc.retrieveProjectBySlug = function(slug) {
return svc.retrieveProjectById(slug)
}
function init(index) {
console.log('init');
return svc.getProjects().then(function(data) {
svc.cachedProjects = data.data.projects;
});
}
return svc;
}]);
describe('Project Service', function() {
beforeEach(module('myApp'));
var ProjectSvc, $httpBackend;
var $q, $rootScope;
beforeEach(inject(function(_ProjectSvc_, _$httpBackend_, _$q_, _$rootScope_) {
// The injector unwraps the underscores (_) from around the parameter names when matching
ProjectSvc = _ProjectSvc_;
$httpBackend = _$httpBackend_;
$q = _$q_;
$rootScope = _$rootScope_;
spyOn(ProjectSvc, 'retrieveProjectBySlug').and.callFake(function() {
var defer = $q.defer();
defer.resolve();
return defer.promise;
});
}));
describe('.retrieveProjectBySlug()', function() {
it('test promise in service', function() {
var project_url = 'https://dl.dropboxusercontent.com/u/2122820/hosted_json/project.json';
//$httpBackend.expectGET(project_url).respond(200, {data: null});
console.log('-------------');
var project;
ProjectSvc.retrieveProjectBySlug('monopoly-checker').then(function(data) {
project = data;
console.log(project);
$rootScope.$apply();
console.log('project', project);
});
expect(project.length).toBe(1);
});
});
});
http://stackoverflow.com/questions/ask#
Running karma start
, I get:
1) test promise in service
Project Service .retrieveProjectBySlug()
TypeError: Cannot read property 'length' of undefined
It seems that the promise never gets resolved, thus project
is always undefined. Can you fix this ?
If it helps, full source code is at https://github.com/rattanakchea/rattanakchea.github.io/tree/test
Upvotes: 0
Views: 852
Reputation: 222498
$rootScope.$apply()
should be called outside of the promise:
ProjectSvc.retrieveProjectBySlug('monopoly-checker').then(function(data) {
project = data;
console.log(project);
console.log('project', project);
});
$rootScope.$apply();
$q
promises are tied to digest cycle, promise chain is called on digest.
Consider using Jasmine promise matchers, they eliminate the need for unwrapping $q
promises in specs.
Upvotes: 1