0

I have an AngularJS service and service name is User. I use it for authentication. Inside this service, I have an authenticate() function which uses Restangular to make a REST call to our API. It's perfectly working in my Angular application. I just started using karma so I can including testing. I'm wondering why I'm getting this error. Also, I've read few stackoverflow post and noticed that they use $httpBackend directly on their spec file. Unfortunately, it's different from my setup.

INFO [watcher]: Changed file "/Users/admin/repos/acme/acme-ui/test/spec/controllers/main.js".
Chrome 41.0.2272 (Mac OS X 10.8.3) App: CockpitApplication factory: GlobalPermissionsFactory should resolve status to 200 FAILED
    Error: Unexpected request: POST https://someinternaldomain.com/v1/authentokens
    No more request expected
        at $httpBackend (/Users/admin/repos/acme/acme-ui/app/bower_components/angular-mocks/angular-mocks.js:1224:9)
        at sendReq (/Users/admin/repos/acme/acme-ui/app/bower_components/angular/angular.js:9538:9)
        at serverRequest (/Users/admin/repos/acme/acme-ui/app/bower_components/angular/angular.js:9255:16)
        at processQueue (/Users/admin/repos/acme/acme-ui/app/bower_components/angular/angular.js:13075:27)
        at /Users/admin/repos/acme/acme-ui/app/bower_components/angular/angular.js:13091:27
        at Scope.$eval (/Users/admin/repos/acme/acme-ui/app/bower_components/angular/angular.js:14291:28)
        at Scope.$digest (/Users/admin/repos/acme/acme-ui/app/bower_components/angular/angular.js:14107:31)
        at Object.<anonymous> (/Users/admin/repos/acme/acme-ui/test/spec/controllers/main.js:71:24)
Firefox 34.0.0 (Mac OS X 10.8) App: CockpitApplication factory: GlobalPermissionsFactory should resolve status to 200 FAILED
    Error: Unexpected request: POST https://someinternaldomain/v1/authentokens
    No more request expected in /Users/admin/repos/acme/acme-ui/app/bower_components/angular-mocks/angular-mocks.js (line 1224)
    $httpBackend@/Users/admin/repos/acme/acme-ui/app/bower_components/angular-mocks/angular-mocks.js:1224:1
    sendReq@/Users/admin/repos/acme/acme-ui/app/bower_components/angular/angular.js:9538:1
    $http/serverRequest@/Users/admin/repos/acme/acme-ui/app/bower_components/angular/angular.js:9255:16
    processQueue@/Users/admin/repos/acme/acme-ui/app/bower_components/angular/angular.js:13075:27
    scheduleProcessQueue/<@/Users/admin/repos/acme/acme-ui/app/bower_components/angular/angular.js:13091:27
    $RootScopeProvider/this.$get</Scope.prototype.$eval@/Users/admin/repos/acme/acme-ui/app/bower_components/angular/angular.js:14291:16
    $RootScopeProvider/this.$get</Scope.prototype.$digest@/Users/admin/repos/acme/acme-ui/app/bower_components/angular/angular.js:14107:15
    @/Users/admin/repos/acme/acme-ui/test/spec/controllers/main.js:71:13
Chrome 41.0.2272 (Mac OS X 10.8.3): Executed 4 of 4 (1 FAILED) (0.114 secs / 0.111 secs)
Firefox 34.0.0 (Mac OS X 10.8): Executed 4 of 4 (1 FAILED) (0.101 secs / 0.092 secs)
TOTAL: 2 FAILED, 6 SUCCESS

and here is my main.js, the Karma spec file

describe('App: CockpitApplication', function () {

  // load the controller's module
    beforeEach(module('CockpitApplication'));

    var $controller, $q, $rootScope;
    beforeEach(inject(function(_$controller_, _$q_, _$rootScope_) {
        $controller = _$controller_;
        $q = _$q_;
        $rootScope = _$rootScope_;
    }));

    describe('factory: GlobalPermissionsFactory', function() {
        var _GlobalPermissionsFactory = null,
            _UserInformationFactory = null,
            _User = null;

        beforeEach(inject(function(GlobalPermissionsFactory) {
            _GlobalPermissionsFactory = GlobalPermissionsFactory;
        }));

        beforeEach(inject(function(UserInformationFactory) {
            _UserInformationFactory = UserInformationFactory;
        }));

        beforeEach(inject(function(User) {
            _User = User;
        }));

        it('should return false', function() {
            expect(_GlobalPermissionsFactory).toBeDefined()
        });

        it('should check if getUserInformation() exists', function() {
            expect(_UserInformationFactory.getUserInformation).toBeDefined()
        });

        it('should return a promise', function() {
            expect(_User.authenticate('admin', 'secret').then).toBeDefined();
        });

        it('should resolve status to 200', function () {
            var data;

            // set up a deferred
            var deferred = $q.defer();
            // get promise reference
            var promise = deferred.promise;

            // set up promise resolve callback
            promise.then(function (response) {
                console.log('data',data);
                data = response.status;
            });

            _User.authenticate('admin', 'secret').then(function(response) {
                // resolve our deferred with the response when it returns
                deferred.resolve(response);
            });

            // force `$digest` to resolve/reject deferreds
            $rootScope.$digest();

            // make your actual test
            expect(data).toEqual(200);

        });
    });
});
devwannabe
  • 3,160
  • 8
  • 42
  • 79

1 Answers1

1

You do need to use $httpBackend - using it like below should work for you.

From the documentation:

During unit testing, we want our unit tests to run quickly and have no external dependencies so we don’t want to send XHR or JSONP requests to a real server. All we really need is to verify whether a certain request has been sent or not, or alternatively just let the application make requests, respond with pre-trained responses and assert that the end result is what we expect it to be.

Using $httpBackend will let you test your service's method in isolation - the success of the test won't depend on the server having to do anything properly. You can also test what the method sends in its request as well.

Without using $httpBackend you are actually making the server call which can lead to a variety of errors. A guess, but I'd say the error you are getting is one of them.

describe('App: CockpitApplication', function () {

  // load the controller's module
    beforeEach(module('CockpitApplication'));

    var $controller, $q, $rootScope, $httpBackend;
    beforeEach(inject(function(_$controller_, _$q_, _$rootScope_, _$httpBackend_) {
        $controller = _$controller_;
        $q = _$q_;
        $rootScope = _$rootScope_;
        $httpBackend = _$httpBackend_;
    }));

    describe('factory: GlobalPermissionsFactory', function() {
        var _GlobalPermissionsFactory = null,
            _UserInformationFactory = null,
            _User = null;

        beforeEach(inject(function(GlobalPermissionsFactory) {
            _GlobalPermissionsFactory = GlobalPermissionsFactory;
        }));

        beforeEach(inject(function(UserInformationFactory) {
            _UserInformationFactory = UserInformationFactory;
        }));

        beforeEach(inject(function(User) {
            _User = User;
        }));

        it('should return false', function() {
            expect(_GlobalPermissionsFactory).toBeDefined()
        });

        it('should check if getUserInformation() exists', function() {
            expect(_UserInformationFactory.getUserInformation).toBeDefined()
        });

        it('should return a promise', function() {
            expect(_User.authenticate('admin', 'secret').then).toBeDefined();     
        });

        it('should resolve status to 200', function () {
            var data;
          
            $httpBackend.whenGET('//url here').respond(200, //return something the server would);
                                                       
            // set up a deferred
            var deferred = $q.defer();
            // get promise reference
            var promise = deferred.promise;

            // set up promise resolve callback
            promise.then(function (response) {
                console.log('data',data);
                data = response.status;
            });

            _User.authenticate('admin', 'secret').then(function(response) {
                // resolve our deferred with the response when it returns
                deferred.resolve(response);
            });
            $httpBackend.flush();
            // force `$digest` to resolve/reject deferreds
            $rootScope.$digest();

            // make your actual test
            expect(data).toEqual(200);

        });
    });
});
lwalden
  • 813
  • 7
  • 11
  • Why? I already have a http call inside User service. I will try it though. – devwannabe Mar 04 '15 at 04:14
  • Added a little more explanation to my answer above. – lwalden Mar 04 '15 at 04:21
  • Got it. It actually work and I don't get the error I posted above. I do have a new error that's related to the authentication. $rootScope.userData.isAuthenticated is undefined. I'll continue troubleshooting. I'm not sure if a service gets instantiated when we inject it within Karma. It's because I initialized that rootscope in my User service. – devwannabe Mar 04 '15 at 04:24