title: Cannot read property '.then' of undefined AngularJS testing promises
body: <p>I have the following AngularJS code which I am trying to test using jasmine ad karma:</p>
  
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>(function() {
    "use strict";
  
    angular
      .module("beckon.steel.beckonstandard.directives.templateDetails", [
        // "beckon.steel.grid.services.beckonGridFactory",
        // "beckon.steel.connectorTemplate.services.connectorTemplateService",
        // "beckon.steel.dashboardTemplate.services.dashboardTemplateService",
        // "beckon.steel.scorecards.services.scorecardTemplateService",
        // "beckon.steel.channel.services.channelService",
        // "beckon.steel.beckonstandard.services.beckonStandardEntityEnum",
        // "beckon.steel.dimensions.dimensionService",
        // "beckon.steel.formulabuilder.services.formulaTemplateService",
      ])
      .directive("templateDetails", function(
        beckonStandardEntityEnum,
        connectorTemplateService,
        dashboardTemplateService,
        scorecardTemplateService,
        channelService,
        dimensionTemplateService,
        formulaTemplateService,
        $stateParams,
        svg,
        bnModalAlertService
      ) {
        return {
          templateUrl: "templateDetails",
          restrict: "E",
          scope: {
            entity: "@",
            baseState: "@",
          },
          controller: function($scope) {
            $scope.backArrow = svg.ARROW_LEFT;
            $scope.connector = null;
            $scope.dashboard = null;
            $scope.scorecard = null;
  
            $scope.icon = svg.ARROW_LEFT;
  
            const showAlert = function(successType, message) {
              bnModalAlertService.clearAlerts().setAlert({
                type: successType,
                msgSafeHtml: message,
              });
            };
  
            const onSuccess = () =&gt; showAlert("success", "Update successful");
            const onFail = (err) =&gt; {
              if (err.data &amp;&amp; err.data.message) {
                showAlert("error", `Update unsuccessful: ${err.data.message}`);
                return;
              }
              showAlert("error", `Update unsuccessful:  ${err.statusText}`);
            };
  
            if ($scope.entity === beckonStandardEntityEnum.CONNECTOR.type) {
              $scope.backState = beckonStandardEntityEnum.CONNECTOR.uiRefState;
  
              connectorTemplateService
                .fetch($stateParams.id)
                .$promise.then(function(connector) {
                  $scope.connector = connector;
                });
  
              $scope.updateConnector = function(connector) {
                connectorTemplateService
                  .update($stateParams.id, connector)
                  .$promise.then(onSuccess)
                  .catch(onFail);
              };
            }
  
          },
        };
      });
  })();</code></pre>
  </div>
  </div>
  </p>
  
  <p>So far I have mocked most of the dependencies but I am having trouble mocking the connectorTemplateService promise.</p>
  
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="true">
  <div class="snippet-code">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>fdescribe("templateDetails", () =&gt; {
    "use strict";
  
    let $scope;
    let $compile;
    let $rootScope;
  
    let beckonStandardEntityEnum;
    let connectorTemplateService;
    let dashboardTemplateService;
    let scorecardTemplateService;
    let channelService;
    let dimensionTemplateService;
    let formulaTemplateService;
    let $stateParams;
    let svg;
    let bnModalAlertService;
    let $q;
    let resolvedValue;
    let deferred;
  
    const getIsolatedScope = (element) =&gt; element.isolateScope() || element.scope();
  
    beforeEach(module("beckon.steel.beckonstandard.directives.templateDetails"));
    beforeEach(
      module(($provide) =&gt; {
        svg = jasmine.createSpy();
        beckonStandardEntityEnum = {
          CONNECTOR: {
            type: "connectortype",
            uiRefState: "connectorUiRefState",
          },
          DASHBOARD: {
            type: "dashboardtype",
            uiRefState: "dashboardUiRefState",
          },
          SCORECARD: {
            type: "scorecardtype",
            uiRefState: "scorecardUiRefState",
          },
          CHANNEL: {
            type: "channeltype",
            uiRefState: "channelUiRefState",
          },
          DIMENSION: {
            type: "dimensiontype",
            uiRefState: "dimensionUiRefState",
          },
          FORMULA: {
            type: "formulatype",
            uiRefState: "formulaUiRefState",
          },
        };
        connectorTemplateService = {
          fetch: () =&gt; {},
          update: () =&gt; {},
        }
  
        bnModalAlertService = {
          clearAlerts: () =&gt; ({
            setAlert: jasmine.createSpy()
          }),
        };
  
        $stateParams = {
          id: "dummyid",
        };
  
        $provide.value("svg", svg);
        $provide.value("beckonStandardEntityEnum", beckonStandardEntityEnum);
        $provide.value("$stateParams", $stateParams);
        $provide.value("connectorTemplateService", connectorTemplateService);
        $provide.value("formulaTemplateService", formulaTemplateService);
        $provide.value("scorecardTemplateService", scorecardTemplateService);
        $provide.value("dimensionTemplateService", dimensionTemplateService);
        $provide.value("channelService", channelService);
        $provide.value("dashboardTemplateService", dashboardTemplateService);
        $provide.value("bnModalAlertService", bnModalAlertService);
      })
    );
  
    beforeEach(
      inject((_$rootScope_, _$compile_, _$q_) =&gt; {
        $compile = _$compile_;
        $rootScope = _$rootScope_;
        $scope = $rootScope.$new(true);
        $q = _$q_;
  
  
      })
    );
  
    describe("when entity matches CONNECTOR type", () =&gt; {
      it("initializes correctly", () =&gt; {
        // Arrange
        const element = angular.element(
          "&lt;template-details entity='connectortype'&gt;Here goes the template&lt;/template-details&gt;"
        );
  
        spyOn(connectorTemplateService, "fetch").and.callFake((id) =&gt; {
          let deferred = $q.defer();
          deferred.resolve("the connector");
          return deferred.promise;
        });
  
  
        spyOn(connectorTemplateService, "update").and.callFake((id) =&gt; {
          let deferred = $q.defer();
          deferred.resolve("success");
          return deferred.promise;
        });
  
        // Act
        $compile(element)($scope);
        $scope.$digest();
        const isolatedScope = getIsolatedScope(element);
        isolatedScope.uiRefState = "connectorUiRefState";
        const expectedValue = "the connector";
  
        isolatedScope.updateConnector();
        isolatedScope.$apply();
  
        // Assertions
  
      });
  
    });
  });</code></pre>
  </div>
  </div>
  </p>
  
  <p>With the way I mocked it(as seen in angular $q docs), I get the error <code>cannot read property '.then' of undefined</code></p>
  
  <p>I have tried to mock it like this </p>
  
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>connectorTemplateService = {
    update: (id, connector) =&gt; ({
      $promise: {
        then: (fn) =&gt; {
          fn();
        },
      },
    }),
    fetch: (id) =&gt; ({
      $promise: {
        then: (fn) =&gt; {
          fn("the connector");
        },
      },
    }),
  };</code></pre>
  </div>
  </div>
  </p>
  
  <p>It worked for the <code>fetch</code> method but not for the <code>update</code> method, because it's not an actual promise and the <code>update</code> method has a <code>catch</code> block. Can anyone show how to mock this promise? </p>