Angular Jasmine test response interceptor

lkartono picture lkartono · Nov 22, 2014 · Viewed 10.5k times · Source

I'm trying to test my response interceptor but I have a hard time figuring out how to mock the $window object. Here is my interceptor code :

'use strict';

angular.module('Domain.handlers')

.config(function($httpProvider) {
  $httpProvider.responseInterceptors.push('UnauthorizedInterceptor');
})

.factory('UnauthorizedInterceptor', function($q, $injector, $window, ENV) {
  return function(promise) {
    var success = function(response) { return response; };
    var error   = function(response) {
      if (response.status === 401) {
        $window.location.href = ENV.account + '/oauth/authorize?client_id=' + ENV.clientId + '&redirect_uri=' + ENV.app + '/oauth/callback&response_type=token';
      }
      return $q.reject(response);
    };
    return promise.then(success, error);
  };
});

And here is my spec :

'use strict';

describe('Domain.handlers.response', function() {
  var UnauthorizedInterceptor,
      httpProvider,
      $httpBackend,
      $http,
      token = '123456789';

  beforeEach(module('Domain.handlers', function($httpProvider) {
    httpProvider = $httpProvider;
  }));

  beforeEach(inject(function(_UnauthorizedInterceptor_, _$httpBackend_, _$http_) {
    UnauthorizedInterceptor = _UnauthorizedInterceptor_;
    $httpBackend = _$httpBackend_;
    $http = _$http_;
  }));

  describe('UnauthorizedInterceptor', function() {
    it('should be defined', function() {
      expect(UnauthorizedInterceptor).toBeDefined();
    });

    describe('HTTP status', function() {
      describe('is 200 OK', function() {
        it('should return a 200 status', function() {
          $httpBackend.expectGET('http://api.domain.com/clients').respond(200, {});
          $http.get('http://api.domain.com/clients');
          $httpBackend.flush();
        });
      });

      describe('is 401 Unauthorized', function() {
        it('should redirect to accounts.domain.com', inject(function($window) {
          $httpBackend.expectGET('http://api.domain.com/clients').respond(401, {});
          $http.get('http://api.domain.com/clients');
          expect($window.location.href).toEqual('http://accounts.domain.com/oauth/.....');
          $httpBackend.flush();
        }));
      });
    });
  });
});

I've got a : Expected 'http://localhost:8080/context.html' to equal 'http://accounts.domain.com/oauth/.....'. Any help on how to mock properly the $window object or more generally how to test a 401 + redirection case?

Answer

user2943490 picture user2943490 · Nov 25, 2014

You should structure your interceptor definition using the more recent syntax. Your URL construction should also be in a service so that it can easily be mocked in tests.

.factory('UnauthorizedInterceptor', function($q, $window, OtherService) {
  var service = {
    responseError: handleUnauthorized
  };

  return service;

  function handleUnauthorized(rejection) {
    if (rejection.status === 401) {
      $window.location.href = OtherService.getUnauthorizedRedirectURL();
    }
    return $q.reject(rejection);
  }
});

Doing so will let you test it just like any other factory without having to worry about the internal implementations of $http interceptors, or having to mock responses with $httpBackend.

describe('Domain.handlers.response', function() {
  var $window,
      UnauthorizedInterceptor,
      OtherService,
      redirectUrl = 'someUrl';

  beforeEach(module('Domain.handlers'));

  beforeEach(function () {
    $window = { location: { href: null } };

    module(function($provide) {
      $provide.value('$window', $window);
    });
  });

  beforeEach(inject(function(_UnauthorizedInterceptor_, _OtherService_) {
    UnauthorizedInterceptor = _UnauthorizedInterceptor_;
    OtherService = _OtherService_;

    spyOn(OtherService, 'getUnauthorizedRedirectURL').andReturn(redirectUrl);
  }));

  describe('UnauthorizedInterceptor', function() {
    it('should be defined', function() {
      expect(UnauthorizedInterceptor).toBeDefined();
    });

    it('should have a handler for responseError', function () {
      expect(angular.isFunction(UnauthorizedInterceptor.responseError)).toBe(true);
    });

    describe('when HTTP 401', function () {
      beforeEach(function () {
        var rejection = { status: 401 };
        UnauthorizedInterceptor.responseError(rejection);
      });

      it('should set window location', function () {
        expect($window.location.href).toBe(redirectUrl);
      });
    });

    describe('when not HTTP 401', function () {
      beforeEach(function () {
        var rejection = { status: 500 };
        UnauthorizedInterceptor.responseError(rejection);
      });

      it('should not set window location', function () {
        expect($window.location.href).not.toBe(redirectUrl);
      });
    });
  });
});