Luk
Luk

Reputation: 431

Testing AngularJs' $http.defaults.headers.common if specific header is set

So I'm new to the world of JavaScript and AngularJS and therefor my code is not as good as it should be yet, but it's improving. Nevertheless I started learning and implementing a simple login page with a REST Backend. After the Login-Form is submitted, a authentication-token is returned and set as a default http-header property like this

$http.defaults.headers.common['X-AUTH-TOKEN'] = data.authToken;

This works fine whenever I test it manually, but that's not the way to go so I'd like to implement a unit-test which checks if the X-AUTH-TOKEN header is set.

Is there a way to check that with $httpBackend? e.g I have the following test:

describe('LoginController', function () {
    var scope, ctrl, $httpBackend;

    // Load our app module definition before each test.
    beforeEach(module('myApp'));

    // The injector ignores leading and trailing underscores here (i.e. _$httpBackend_).
    // This allows us to inject a service but then attach it to a variable
    // with the same name as the service.
    beforeEach(inject(function (_$httpBackend_, $rootScope, $controller) {
        $httpBackend = _$httpBackend_;
        scope = $rootScope.$new();
        ctrl = $controller('LoginController', {$scope: scope}, {$http: $httpBackend}, {$location: null});
    }));

    it('should create an authToken and set it', function () {
        $httpBackend.expectPOST('http://localhost:9000/login', '200').respond(200, '{"authToken":"52d29fd63004c92b972f6b99;65e922bc-5e33-4bdb-9d52-46fc352189fe"}');
        scope.login('200');
        $httpBackend.flush();

        expect(scope.data.authToken).toBe('52d29fd63004c92b972f6b99;65e922bc-5e33-4bdb-9d52-46fc352189fe');
        expect(scope.loginValidationOverallError).toBe(false);
        expect(scope.status).toBe(200);
    });

My Controller looks like this:

.controller('LoginController', ['$scope', '$http', '$location',
    function ($scope, $http, $location) {

        // Login Stuff
        $scope.data = {};
        $scope.status = {};
        $scope.loginValidationOverallError = false;
        $scope.login = function (user) {
            $http.post('http://localhost:9000/login', user).success(function (data, status) {

                $scope.data = data;
                $scope.status = status;
                $scope.loginValidationOverallError = false;


                console.log($scope.status, $scope.data);
                $http.defaults.headers.common['X-AUTH-TOKEN'] = data.authToken;
                $location.path('/user');

            }).error(function (data, status) {
                    console.log(status + ' error');
                    $scope.loginValidationOverallError = true;
                });

        };
        ...

I checked the documentation at http://docs.angularjs.org/api/ngMock.$httpBackend but am not sure if the last test is actually applicable to my code (and how that code actually tests something)

it('should send auth header', function() {
    var controller = createController();
    $httpBackend.flush();

    $httpBackend.expectPOST('/add-msg.py', undefined, function(headers) {
        // check if the header was send, if it wasn't the expectation won't
        // match the request and the test will fail
        return headers['Authorization'] == 'xxx';
    }).respond(201, '');

    $rootScope.saveMessage('whatever');
    $httpBackend.flush();
});

Upvotes: 5

Views: 7725

Answers (1)

doanduyhai
doanduyhai

Reputation: 8812

I was facing the same issue and I finally solved it. It was very tricky

Souce code for AuthenticationService.login() function

$http.post(...)
  .success(function(data) {
     ...
     $http.defaults.headers.common['Authorization'] = data.oauth_token;
   });

Test code

beforeEach(inject(function(_$httpBackend_,AuthenticationService) {
  $httpBackend = _$httpBackend_;
  $authenticationService = AuthenticationService;
}));

it('should login successfully with correct parameter', inject(function($http) {
// Given
...
...
var fakeResponse = {
  access_token: 'myToken'
}

$httpBackend.expectPOST('oauth/token',urlEncodedParams, function(headers) {
      return headers['Content-Type'] ===  'application/x-www-form-urlencoded';
}).respond(200, fakeResponse);

// When
$authenticationService.login(username,password); 


// Then
$httpBackend.flush();
expect($http.defaults.headers.common['Authorization']).toBe('myToken');

The trick here is that the default header is set on the real $http service, not the mocked $httpBackend. That's why you should inject the real $http service

I've tried testing the $httpBackend but got an "undefined" error because $httpBackend does not have 'defaults' property

Upvotes: 8

Related Questions