0

I'm using the $httpBackend service to mock requests for my Service tests. However, I began getting errors in my tests that suggested the requests were actually being made vs being mocked. When I commented out the catch all route I provided in my app config the tests all passed. Can anyone explain why this is happening? Below is my code and errors.

EDIT Edited to include Whisher's snippet but not sure im using it correctly.

describe('AssetManager', function() {

var state, $httpBackend, AssetManager;

// If the app isn't broken into smaller modules,
// we must load the entire app for each test
beforeEach(function() {
  module('us.assetManager');
  module('statemock');

  inject(function($state, _$httpBackend_, _AssetManager_) {
    state = $state;
    $httpBackend = _$httpBackend_;
    AssetManager = _AssetManager_;
  });

});

afterEach(function() {
  $httpBackend.verifyNoOutstandingExpectation();
  $httpBackend.verifyNoOutstandingRequest();
});

describe('#getSpendAccounts', function() {

  beforeEach(function() {
    $httpBackend.expectGET('http://localhost:3000/api/facebook/spend_accounts.json').respond([
      {email: 'me@unifiedsocial.com'}
    ]);
  });

  it('fetches the users Spend Accounts', function() {
    var result;

    state.expectTransitionTo('fbCustomAudiences');

    AssetManager.getSpendAccounts('facebook').then(function(data) {
      result = data;
    });

    $httpBackend.flush();

    expect(result.data[0].email).toBe('me@unifiedsocial.com');
  });

});

app.js

(function() {
  'use strict';

   angular
     .module('us.assetManager', ['ui.router'])
     .config(function ($stateProvider, $urlRouterProvider) {
       // catch all route
      $urlRouterProvider.otherwise('/facebook/custom_audiences');

  $stateProvider
    .state('fbCustomAudiences', {
      abstract: true,
      templateUrl: '/common/views/layout.html',
      controller: 'CustomAudiencesCtrl as audiences',
      data: {
        network: 'facebook'
      },
      resolve: {
        getSpendAccounts: function(AssetManager) {
          return AssetManager.getSpendAccounts('facebook');
        }
      }
    })
    .state('fbCustomAudiences.root', {
      url: '/facebook/custom_audiences',
      views: {
        'breadcrumbs': {
          templateUrl: 'facebook/views/breadcrumbs.html'
        },
        'sidebar': {
          templateUrl: 'facebook/views/sidebar.html'
        },
        'acctControls': {
          templateUrl: 'facebook/views/controls.html'
        },
        'table': {
          templateUrl: 'facebook/views/custom-audiences-table.html'
        }
      }
    })
    .state('fbCustomAudiences.spendAccount', {
      url: '/facebook/custom_audiences/:spendAccountId',
      views: {
        'breadcrumbs': {
          templateUrl: 'facebook/views/breadcrumbs.html'
        },
        'sidebar': {
          templateUrl: 'facebook/views/sidebar.html'
        },
        'acctControls': {
          templateUrl: 'facebook/views/controls.html'
        },
        'table': {
          templateUrl: 'facebook/views/custom-audiences-table.html'
        }
      },
      resolve: {
        getAdAccounts: function(getSpendAccounts, AssetManager, $stateParams) {
          var spendAccountId = parseInt($stateParams.spendAccountId);

          // set the selectedSpendAccount for the dropdown
          AssetManager.selectedSpendAccount = _.find(getSpendAccounts.data, function(spendAccount) {
            return spendAccount.id === spendAccountId;
          });

          return AssetManager.getAdAccounts('facebook', spendAccountId);
        }
      }
    })
    .state('fbCustomAudiences.spendAccount.adAccount', {
      url: '/:adAccountId',
      views: {
        'breadcrumbs': {
          templateUrl: 'facebook/views/breadcrumbs.html'
        },
        'sidebar': {
          templateUrl: 'facebook/views/sidebar.html'
        },
        'acctControls': {
          templateUrl: 'facebook/views/controls.html'
        },
        'table': {
          templateUrl: 'facebook/views/custom-audiences-table.html'
        }
      },
      resolve: {
        getCustomAudiences: function(getAdAccounts, AssetManager, $stateParams) {
          var adAccountId = parseInt($stateParams.adAccountId);

          // set the selectedAdAccount for the dropdown
          AssetManager.selectedAdAccount = _.find(getAdAccounts.data, function(adAccount) {
            return adAccount.id === adAccountId;
          });

          return AssetManager.getAudiencesSummaryData('facebook', adAccountId);
        }
      }
    })
  });
})();

AssetManager.js

(function() {
'use strict';

angular
  .module('us.assetManager')
  .factory('AssetManager', AssetManager);


AssetManager.$inject = ['$http', '$state'];

function AssetManager($http, $state) {

// domain for HTTP endpoints
// StackOverflow doesnt allow localhost so just wrote domain
var domain = 'http://domain:3000/api/';

var spendAccounts = [],
    adAccounts = [],
    audiencesSummaryData = [],
    selectedSpendAccount = {},
    selectedAdAccount = {},
    selectedAudience = {},
    placeholder = null;

var factory = {
  spendAccounts: spendAccounts,
  adAccounts: adAccounts,
  selectedSpendAccount: selectedSpendAccount,
  audiencesSummaryData: audiencesSummaryData,
  selectedAdAccount: selectedAdAccount,
  selectedAudience: selectedAudience,
  placeholder: placeholder,
  getSpendAccounts: getSpendAccounts,
  getAdAccounts: getAdAccounts,
  getAudiencesSummaryData: getAudiencesSummaryData,
  getAudience: getAudience,
  createAudience: createAudience,
  editAudience: editAudience,
  deleteAudience: deleteAudience,
  setSelectedSpendAccount: setSelectedSpendAccount,
  setSelectedAdAccount: setSelectedAdAccount,
  hasSelectedSpendAccount: hasSelectedSpendAccount,
  hasSelectedAdAccount: hasSelectedAdAccount,
  hasSelectedAccounts: hasSelectedAccounts,
  setPlaceholder: setPlaceHolder
};

return factory;

// all URLs will change domains

function getSpendAccounts(socialNetwork) {
  var url = domain + socialNetwork + '/spend_accounts.json',
      AssetManager = this;

  return $http.get(url)
    .success(function(data) {
      AssetManager.spendAccounts = data;
    })
    .error(function() {
      console.log('Could not retrieve Spend Accounts.');
    });
}

function getAdAccounts(socialNetwork, spendAccountId) {
  var url = domain + socialNetwork + '/ad_accounts/' + spendAccountId + '.json'
      AssetManager = this;

  return $http.get(url)
    .success(function(data) {
      AssetManager.adAccounts = data;
    })
    .error(function() {
      console.log('Could not retrieve Ad Accounts.');
    });
}

function getAudiencesSummaryData(socialNetwork, adAccountId) {
  var url = domain + socialNetwork + '/audiences_summary_data/' + adAccountId + '.json',
      AssetManager = this;

  return $http.get(url)
    .success(function(data) {
      AssetManager.audiencesSummaryData = data;
    })
    .error(function() {
      console.log('Could not retrieve Audiences summary data.');
    });
}

function getAudience(socialNetwork, adAccountId, audienceId) {
  var url = domain + socialNetwork + '/audience/' + adAccountId + '/' + audienceId + '.json',
      AssetManager = this;

  return $http.get(url)
    .success(function(data) {
      AssetManager.selectedAudience = data;
    })
    .error(function() {
      console.log('Could not retrieve Audience with id: ' + audienceId);
    });
}

AssetManager.spec.js

(function() {
'use strict';

describe('AssetManager', function() {

var AssetManager, $httpBackend;

// If the app isn't broken into smaller modules,
// we must load the entire app for each test
beforeEach(function() {
  module('us.assetManager');

  inject(function(_$httpBackend_, _AssetManager_) {
    $httpBackend = _$httpBackend_;
    AssetManager = _AssetManager_
  });

});

afterEach(function() {
  $httpBackend.verifyNoOutstandingExpectation();
  $httpBackend.verifyNoOutstandingRequest();
});

describe('#getSpendAccounts', function() {

  beforeEach(function() {
    $httpBackend.expectGET('http://localhost:3000/api/facebook/spend_accounts.json');
    $httpBackend.whenGET('http://localhost:3000/api/facebook/spend_accounts.json').respond([
      {email: 'me@unifiedsocial.com'}
    ]);
  });

  it('fetches the users Spend Accounts', function() {
    var result;

    AssetManager.getSpendAccounts('facebook').then(function(data) {
      result = data;
    });

    $httpBackend.flush();

    expect(result.data[0].email).toBe('me@unifiedsocial.com');
  });

});

describe('#getAdAccounts', function() {

  beforeEach(function() {
    $httpBackend.expectGET('http://localhost:3000/api/facebook/ad_accounts/1.json').respond([
      {
        "id": 1,
        "spend_account_id": 1,
        "name": "Unified",
        "initiatives": 3,
        "audiences": 5,
        "pixels": 5,
        "spend": 25000
      }
    ]);
  });

  it('fetches the Spend Accounts Ad Accounts', function() {
    var result;

    AssetManager.getAdAccounts('facebook', '1').then(function(data) {
      result = data;
    });

    $httpBackend.flush();

    expect(result.data[0].name).toBe('Unified');
  });

});

describe('#getAudiencesSummaryData', function() {

  beforeEach(function() {
    $httpBackend.expectGET('http://localhost:3000/api/facebook/audiences_summary_data/1.json').respond([
      {
        'id': 'facebook_id1',
        'name': 'A Name of audience',
        'source': {
          'type': 'Website',
          'detail': 'Custom Audience'
        },
        'count': 2500,
        'timeCreated': new Date('Fri Aug 08 2014 10:29:43 GMT-0400 (EDT)'),
        'status': {
          'deliveryStatus': 'NOT READY',
          'deliveryDescription': 'Audience is < 20 people',
          'operationStatus': 'Operation status??',
          'operationDescription': 'Operation description??'
        }
      },
      {
        'id': 'facebook_id2',
        'name': 'B Name of audience',
        'source': {
          'type': 'Lookalike',
          'detail': 'Custom Audience'
        },
        'count': 2700,
        'timeCreated': new Date('Fri Aug 05 2014 10:29:43 GMT-0400 (EDT)'),
        'status': {
          'deliveryStatus': 'READY',
          'deliveryDescription': 'Audience is < 20 people',
          'operationStatus': 'Operation status??',
          'operationDescription': 'Operation description??'
        }
      }
    ]);
  });

  it('fetches the custom audience summary data', function() {
    var result;

    AssetManager.getAudiencesSummaryData('facebook', '1').then(function(data) {
      result = data;
    });

    $httpBackend.flush();

    expect(result.data[0].name).toBe('A Name of audience');
  });

});

ERRORS ARE SAME FOR ALL REQUESTS

PhantomJS 1.9.7 (Mac OS X) AssetManager #getSpendAccounts fetches the users Spend Accounts FAILED
Error: Unexpected request: GET http://localhost:3000/api/facebook/spend_accounts.json
No more request expected
    at $httpBackend (/Users/evankline/unified/asset-manager/bower_components/angular-mocks/angular-mocks.js:1179)
    at sendReq (/Users/evankline/unified/asset-manager/bower_components/angular/angular.js:8334)
    at /Users/evankline/unified/asset-manager/bower_components/angular/angular.js:8066
    at /Users/evankline/unified/asset-manager/bower_components/angular/angular.js:11546
    at /Users/evankline/unified/asset-manager/bower_components/angular/angular.js:11546
    at /Users/evankline/unified/asset-manager/bower_components/angular/angular.js:11632
    at /Users/evankline/unified/asset-manager/bower_components/angular/angular.js:12658
    at /Users/evankline/unified/asset-manager/bower_components/angular/angular.js:12470
    at /Users/evankline/unified/asset-manager/bower_components/angular-mocks/angular-mocks.js:1438
    at /Users/evankline/unified/asset-manager/test/spec/common/services/AssetManager.spec.js:47
Error: Unexpected request: GET /common/views/layout.html
No more request expected
    at $httpBackend (/Users/evankline/unified/asset-manager/bower_components/angular-mocks/angular-mocks.js:1179)
    at sendReq (/Users/evankline/unified/asset-manager/bower_components/angular/angular.js:8334)
    at /Users/evankline/unified/asset-manager/bower_components/angular/angular.js:8066
    at /Users/evankline/unified/asset-manager/bower_components/angular/angular.js:11546
    at /Users/evankline/unified/asset-manager/bower_components/angular/angular.js:11546
    at /Users/evankline/unified/asset-manager/bower_components/angular/angular.js:11632
    at /Users/evankline/unified/asset-manager/bower_components/angular/angular.js:12658
    at /Users/evankline/unified/asset-manager/bower_components/angular/angular.js:12470
    at /Users/evankline/unified/asset-manager/bower_components/angular-mocks/angular-mocks.js:1470
    at /Users/evankline/unified/asset-manager/test/spec/common/services/AssetManager.spec.js:28
evkline
  • 1,451
  • 3
  • 16
  • 34
  • take a look at http://stackoverflow.com/questions/23655307/ui-router-interfers-with-httpbackend-unit-test-angular-js .btw why do you use the real service if you use mock ? – Whisher Aug 23 '14 at 18:38
  • @Whisher thanks for that! Is there any explanation as to why this happens? – evkline Aug 23 '14 at 18:40
  • sorry but it's the only usefull link I've found out there for ui-router test issue :( btw I think you need only whenGET – Whisher Aug 23 '14 at 18:42
  • http://stackoverflow.com/questions/25387375/karma-angularjs-how-to-test-run-block-with-service it's not for your issue but may be can help – Whisher Aug 23 '14 at 18:43
  • @Whisher a question on how to use this... does the expectTransitionTo take the abstract state in my case (fbCustomAudiences) or the route that would get initialized at the url the the $urlRouterProvider redirects to (fbCustomAudiences.root)... also I thought whenGET was for creating a mock API for e2e testing and that expectGET was used to test whether or not a request was made https://docs.angularjs.org/api/ngMock/service/$httpBackend#request-expectations-vs-backend-definitions? Do you find using 1 works better than the other? – evkline Aug 23 '14 at 18:47
  • sorry but me too I'm quite new to testing so it's better waiting for a pro :) – Whisher Aug 23 '14 at 18:49
  • @Whisher I added an edit to a single test... does it seem like im using it correctly? – evkline Aug 23 '14 at 18:53
  • Scratch that it workS!!!!! – evkline Aug 23 '14 at 18:54
  • great to see that :) – Whisher Aug 23 '14 at 18:55
  • Whisher if u want to throw an answer up id be happy to +1 you! Thanks again! Ive got to run but will +1 when u when I get back – evkline Aug 23 '14 at 18:56
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/59863/discussion-between-whisher-and-evkline). – Whisher Aug 23 '14 at 19:05

1 Answers1

0

Urls in your code and in your test are different. In test you mocked request to

'http://localhost:3000/api/facebook/spend_accounts.json'

but according to your code

var domain = 'http://domain:3000/api/';    
...
function getSpendAccounts(socialNetwork) {
      var url = domain + socialNetwork + '/spend_accounts.json',
          AssetManager = this;
    //do request..
    }

url should be

'http://domain:3000/api/facebook/spend_accounts.json'
ne4istb
  • 662
  • 5
  • 17