title: Testing Angular Service that returns a promise
body: <p>I use Karma + Jasmine to write Unit Test for an angular project.</p>
  
  <p>I am trying to test an Angular service that returns the correct result via a promise. Below are what I have so far.</p>
  
  <p>Project Service</p>
  
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true">
  <div class="snippet-code">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>'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 &lt; svc.cachedProjects.length; i++){
              if (svc.cachedProjects[i].slug &amp;&amp; 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 &gt; 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;
  
  
  
  }]);</code></pre>
  </div>
  </div>
  </p>
  
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true">
  <div class="snippet-code">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>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);
      });
    });
  });</code></pre>
  <pre class="snippet-code-html lang-html prettyprint-override"><code>http://stackoverflow.com/questions/ask#</code></pre>
  </div>
  </div>
  </p>
  
  <p>Running <code>karma start</code>, I get:</p>
  
  <pre><code>1) test promise in service
   Project Service .retrieveProjectBySlug()
   TypeError: Cannot read property 'length' of undefined
  </code></pre>
  
  <p>It seems that the promise never gets resolved, thus <code>project</code> is always undefined. Can you fix this ?</p>
  
  <p>If it helps, full source code is at <a href="https://github.com/rattanakchea/rattanakchea.github.io/tree/test" rel="nofollow">https://github.com/rattanakchea/rattanakchea.github.io/tree/test</a></p>