title: Fake $resource $save function in angularjs jasmine controller unit test
body: <p>I'm writing unit tests for angularjs using jasmine framework. There is a userService which is used by the controller and which I need to fake.
  I know that I can use jasmine's spyOn functionality, but I've decided to go with different approach (fake the whole service). 
  Problem is that I cannot correctly fake $save function and controller doesn't see it.</p>
  
  <p>I'm including only relevant bits of code. </p>
  
  <p>Here's real UserService which uses $resource</p>
  
  <pre><code>    userService.factory("UserService", ["$resource",
      function($resource) {
  
          var api = "api/AdminApi/";
  
          return $resource(
  
              // default api
              api + ":id",
  
              // custom actions
              {
                  GetGroups: {
                      url: api + "GetGroups",
                      method: "GET",
                      isArray: true
                  }
              });
      }
  </code></pre>
  
  <p>Here's the fake mocked service:</p>
  
  <pre><code>var mockUserService = {
  
  // get
  query: function() {
      return this._users;
  },
  
  // post
  $save: function (user) {        // SOMETHING WRONG IN THIS FUNCTION?
      var deferred = $q.defer();
      deferred.resolve(user);
      return deferred.promise;
  },
  
  GetGroups: function() {
      return this._groups;
  },
  
  // these are "private".
  _users: [],
  _groups: [],
  _loggedInUser: {},
  $q: null, ...
  </code></pre>
  
  <p>Here's test code</p>
  
  <pre><code>var $scope;
  beforeEach( function() {
  
      module("adminApp", function ($provide) {
      });
  
      inject(function ($controller, $rootScope, $q) {
  
          $scope = $rootScope.$new();  
          $controller("GroupController", {
              $scope:         $scope,
              UserService:    mockUserService.Initialize($q)
          });
  
          // resolve promises   
          $scope.$digest();
      });
  });
  
  describe("test.", function () {
  
      describe("Groups.", function () {
  
          it("Should add user to administators group", function () {
  
              var testUser = getUserByName($scope.Users, "JustUser");
              var administratorsGroup = getGroupByName($scope.Groups, "Administrators");
              expect(userHasGroup(testUser, administratorsGroup)).toBeFalsy();
  
              $scope.SelectGroup(1, administratorsGroup); // first we should select group
              $scope.ToggleGroupForUser(testUser);
  
              expect(userHasGroup(testUser, administratorsGroup)).toBeThuthy();
  
          });
      });
  </code></pre>
  
  <p>A part of controller code that fails. 
  Message: TypeError: undefined is not a constructor (evaluating 'userCopy.$save'). 
  For some reason $save function is not being faked correctly as controller doesn't see it in mockUserService. </p>
  
  <pre><code>var userCopy = angular.copy(user);
  
              if (userHasGroup(user, $scope.SelectedGroup)) {
                  deleteGroupFromUser(userCopy, $scope.SelectedGroup);
  
              } else {
                  userCopy.Groups.push($scope.SelectedGroup);
              }
  
              $scope.UserPromise = userCopy.$save( // CODE FAILS ON THIS LINE, probably because $save function is not faked correctly 
                  function() {
  
                      substituteUserByUpdatedVersion(userCopy);
                      updateUserList();
                  },
                  function() {
                      alert("error");
                  });
  </code></pre>
  
  <p>I'm using angular 1.4.8 and jasmine 2.4.1.
  Thanks in advance</p>
  
  <p><strong>UPDATE</strong>
  I figured out that the problem exists only with $resource non-get instance actions (those that are invoked with dollar sign), not class actions. From $resource documentation: </p>
  
  <ul>
  <li>HTTP GET "class" actions: Resource.action([parameters], [success], [error])</li>
  <li>non-GET "class" actions: Resource.action([parameters], postData, [success], [error])</li>
  <li>non-GET instance actions: instance.$action([parameters], [success], [error])</li>
  </ul>
  
  <p>Still haven't found a solution</p>