Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use $httpBackend to mock $http calls change the expected url Angular

Tags:

I am trying to test a service which is using $http

 var APIClient = function($http) {      this.send = function(data) {          $http({              method: data.method,              url: data.url,              headers: data.headers,              data: data.data          }).success(function(response, status) {              data.success(response, status);          }).error(function(response, status) {              data.error(response, status);          });      }  }   angular.module('api.client', []).factory('APIClient', ['$http'      function($http) {          var client = new APIClient($http);           return {              send: function(data) {                  return client.send(data);              },          }       }  ]); 

And the test

  describe('send', function() {        var apiClient, $httpBackend;        beforeEach(module('compare'));        beforeEach(inject(function($injector) {           $httpBackend = $injector.get('$httpBackend');           apiClient = $injector.get('APIClient');       }));        it('Should check if send() exists', function() {           expect(apiClient.send).toBeDefined();       });        it('Should send GET request', function(done) {           var url = '/';            $httpBackend.expect('GET', url).respond({});            apiClient.send({               url: url,               success: function(data, status) {                   console.log(status);                   done();               },               error: function(data, status) {                   console.log(status);                   done();               }           });            $httpBackend.flush();       });   }); 

But I always have this error

PhantomJS 1.9.8 (Mac OS X) send Should send GET request FAILED         Error: Unexpected request: GET templates/test.html         Expected GET / 

The expected url is always the last state in my app.js In this case

// Ionic Starter App  // angular.module is a global place for creating, registering and retrieving Angular modules // 'starter' is the name of this angular module example (also set in a <body> attribute in index.html) // the 2nd parameter is an array of 'requires' // 'starter.services' is found in services.js // 'starter.controllers' is found in controllers.js  angular.module('compare',     [         'ionic',         'manager.user',         'api.client',         'api.user',         'api.compare',         'user.controllers',         'test.controllers'     ] )      .run(function ($ionicPlatform) {         $ionicPlatform.ready(function () {             // Hide the accessory bar by default (remove this to show the accessory bar above the keyboard             // for form inputs)             if (window.cordova && window.cordova.plugins && window.cordova.plugins.Keyboard) {                 cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);             }             if (window.StatusBar) {                 // org.apache.cordova.statusbar required                 StatusBar.styleLightContent();             }         });     })      .config(function ($stateProvider, $urlRouterProvider) {          // Ionic uses AngularUI Router which uses the concept of states         // Learn more here: https://github.com/angular-ui/ui-router         // Set up the various states which the app can be in.         // Each state's controller can be found in controllers.js         $stateProvider              // setup an abstract state for the tabs directive             .state('tab', {                 url: "/tab",                 abstract: true,                 templateUrl: "templates/tabs.html"             })              // Each tab has its own nav history stack:              .state('tab.dash', {                 url: '/dash',                 views: {                     'tab-dash': {                         templateUrl: 'templates/tab-dash.html',                         controller: 'DashCtrl'                     }                 }             })              .state('subscription', {                 url: '/subscription',                 templateUrl: 'templates/subscription.html',                 controller: 'SubscriptionCtrl'             })              .state('login', {                 url: '/login',                 templateUrl: 'templates/login.html',                 controller: 'LoginCtrl'             })              .state('test-compare', {                 url: '/test/compare',                 templateUrl: 'templates/test.html',                 controller: 'TestCompareCtrl'             })          // if none of the above states are matched, use this as the fallback         $urlRouterProvider.otherwise('/login');      }); 

I don't understand why the url is changing I am giving / and it test templates/test.html which is always the last state template

like image 843
Ajouve Avatar asked Jul 01 '15 09:07

Ajouve


2 Answers

Your main issue here is this line:

beforeEach(module('compare')); 

You are loading your entire app here instead of just the apiClient. Essentially, you are doing a full blown integration test, instead of a unit test.

You should be loading only api.client.

beforeEach(module('api.client')); 

Something useful to note, you could also do something like:

$httpBackend.whenGET(/templates\/(.*)/).respond(''); which basically ignores all templates that get loaded by routers, controllers, or directives. If you do this though, it still wouldn't be considered a unit test, because you are not strictly testing just your APIClient.

Another useful note:

Anything you execute inside of .run or .config should not be an annonymous function, that way you could mock it.

An example of this would be doing:

.config(CompareStateLoader);  CompareStateLoader.$inject = [     '$stateProvider',      '$urlRouterProvider' ];  function CompareStateLoader(     $stateProvider,      $urlRouterProvider ){     //configure states here  } 

Doing this would allow you to mock CompareStateLoader and load this in your test runner.

For more information on this, please see John Papa's Angular Style Guide here.

like image 121
Travis Avatar answered Oct 06 '22 01:10

Travis


I would suggest to compile all your templates into JS file (for example with grunt "html2js" task or karma preprocessor "ng-html2js") and do not have headache with GETing templates.

Or you also could use passThrough

$httpBackend.when('GET', /\.html$/).passThrough() 

Example - http://plnkr.co/edit/pbjcDl?p=preview

But I would suggest to use first option.

like image 39
Stepan Suvorov Avatar answered Oct 06 '22 01:10

Stepan Suvorov