title: Test a controller with success() and error ()
body: <p>I'm trying to work out the best way to unit test success and error callbacks in controllers. I am able to mock out service methods, as long as the controller only uses the default $q functions such as 'then' (see the example below). I'm having an issue when the controller responds to a 'success' or 'error' promise. (Sorry if my terminology is not correct).</p>
  
  <p>Here is an example controller \ service </p>
  
  <pre><code>var myControllers = angular.module('myControllers');
  
  myControllers.controller('SimpleController', ['$scope', 'myService',
    function ($scope, myService) {
  
        var id = 1;
        $scope.loadData = function () {
            myService.get(id).then(function (response) {
                $scope.data = response.data;
            });
        };
  
        $scope.loadData2 = function () {
            myService.get(id).success(function (response) {
                $scope.data = response.data;
            }).error(function(response) {
                $scope.error = 'ERROR';
            });
        }; 
    }]);
  
  
  cocoApp.service('myService', [
      '$http', function($http) {
          function get(id) {
              return $http.get('/api/' + id);
          }
      }
  ]);  
  </code></pre>
  
  <p>I have the following test</p>
  
  <pre><code>'use strict';
  
  describe('SimpleControllerTests', function () {
  
      var scope;
      var controller;
      var getResponse = { data: 'this is a mocked response' };
  
      beforeEach(angular.mock.module('myApp'));
  
      beforeEach(angular.mock.inject(function($q, $controller, $rootScope, $routeParams){
  
          scope = $rootScope;
          var myServiceMock = {
              get: function() {}
          };
  
          // setup a promise for the get
          var getDeferred = $q.defer();
          getDeferred.resolve(getResponse);
          spyOn(myServiceMock, 'get').andReturn(getDeferred.promise);
  
          controller = $controller('SimpleController', { $scope: scope, myService: myServiceMock });
      }));
  
  
      it('this tests works', function() {
          scope.loadData();
          expect(scope.data).toEqual(getResponse.data);
      });
  
      it('this doesnt work', function () {
          scope.loadData2();
          expect(scope.data).toEqual(getResponse.data);
      });
  });
  </code></pre>
  
  <p>The first test passes and the second fails with the error "TypeError: Object doesn't support property or method 'success'". I get that in this instance that getDeferred.promise
  does not have a success function. Okay here is the question, what is a nice way to write this test so that I can test the 'success', 'error' &amp; 'then' conditions of a mocked service ?</p>
  
  <p>I'm starting to think that I should avoid the use of success() and error() in my controllers...</p>
  
  <p><strong>EDIT</strong></p>
  
  <p>So after thinking about this some more, and thanks to the detailed answer below, <strong>I've come to the conclusion that the handling the success and error callbacks in the controller is bad.</strong> As HackedByChinese mentions below success\error is syntactic sugar that is added by $http. So, in actual fact, by trying to handle success \ error I am letting $http concerns leak into my controller, which is exactly what I was trying to avoid by wrapping the $http calls in a service. The approach I'm going to take is to change the controller not to use success \ error:</p>
  
  <pre><code>myControllers.controller('SimpleController', ['$scope', 'myService',
    function ($scope, myService) {
  
        var id = 1;
        $scope.loadData = function () {
            myService.get(id).then(function (response) {
                $scope.data = response.data;
            }, function (response) {
                $scope.error = 'ERROR';
            });
        };
    }]);
  </code></pre>
  
  <p>This way I can test the error \ success conditions by calling resolve() and reject() on the deferred object:</p>
  
  <pre><code>'use strict';
  
  describe('SimpleControllerTests', function () {
  
      var scope;
      var controller;
      var getResponse = { data: 'this is a mocked response' };
      var getDeferred;
      var myServiceMock;
  
      //mock Application to allow us to inject our own dependencies
      beforeEach(angular.mock.module('myApp'));
      //mock the controller for the same reason and include $rootScope and $controller
      beforeEach(angular.mock.inject(function($q, $controller, $rootScope, $routeParams) {
  
          scope = $rootScope;
          myServiceMock = {
              get: function() {}
          };
          // setup a promise for the get
          getDeferred = $q.defer();
          spyOn(myServiceMock, 'get').andReturn(getDeferred.promise);
          controller = $controller('SimpleController', { $scope: scope, myService: myServiceMock });  
      }));
  
      it('should set some data on the scope when successful', function () {
          getDeferred.resolve(getResponse);
          scope.loadData();
          scope.$apply();
          expect(myServiceMock.get).toHaveBeenCalled();
          expect(scope.data).toEqual(getResponse.data);
      });
  
      it('should do something else when unsuccessful', function () {
          getDeferred.reject(getResponse);
          scope.loadData();
          scope.$apply();
          expect(myServiceMock.get).toHaveBeenCalled();
          expect(scope.error).toEqual('ERROR');
      });
  });
  </code></pre>