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
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With