单元测试Angular HTTP拦截器

时间:2016-09-13 16:23:15

标签: javascript angularjs unit-testing karma-jasmine

我有一个标准的HTTP拦截器作为工厂:

angular
  .module('app.services')
  .factory('HttpInterceptorService', HttpInterceptorService);

function HttpInterceptorService($injector) {

  // Callable functions
  var service = {
    response: response,
    responseError: responseError
  };

  return service;

  // Pass through clean response
  function response(data) {
    return data;
  }

  // Handle error response
  function responseError(rejection) {

    // Handle bypass requests
    if (angular.isDefined(rejection.config) && rejection.config.bypassInterceptor) {
      return rejection;
    }

    // Get $state via $injector to avoid a circular dependency
    var state = $injector.get('$state');

    switch (rejection.status) {
      case 404:
        return state.go('404');
        break;
      default:
        return state.go('error');
    }
  }

}

在手动测试中,如果HTTP调用返回错误响应,我可以通过将用户重定向到相关的404或错误页面来正常工作。这里的基本原理由Angular记录:https://docs.angularjs.org/api/ng/service/ $ http #interceptors

现在我试图用Karma& amp; Jasmine测试responseError函数是否正常工作。我已经检查了this SO answer来帮助我。我的测试看起来像这样:

describe('HttpInterceptorService', function() {

  // Bindable members
  var $window,
      HttpInterceptorService;

  // Load module
  beforeEach(module('app.services'));

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

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

  // Bind references to global variables
  beforeEach(inject(function(_HttpInterceptorService_) {
    HttpInterceptorService = _HttpInterceptorService_;
  }));

  // Check service exists with methods
  it('Exists with required methods', function() {
    expect(HttpInterceptorService).toBeDefined();
    expect(angular.isFunction(HttpInterceptorService.response)).toBe(true);
    expect(angular.isFunction(HttpInterceptorService.responseError)).toBe(true);
  });

  // Test 404 HTTP response
  describe('When HTTP response 404', function () {
    beforeEach(function() {
      HttpInterceptorService.responseError({ status: 404 });
    });

    it('Sets window location', function () {
      expect($window.location.href).toBe('/404');
    });
  });

});

我的测试通过Exists with required methods检查,但Sets window location失败,出现以下错误:

Error: [$injector:unpr] Unknown provider: $stateProvider <- $state

1 个答案:

答案 0 :(得分:1)

该模块似乎没有加载ui.router模块,因此$state服务未定义。这很好,因为真正的路由器引入了额外的移动部件,在单元测试中是非常不受欢迎的。

对于功能测试,将单位视为黑盒,提供初始条件并测试结果是正常的,断言window.location是合适的。

对于单元测试,无需将单元视为黑盒,$state服务可能存在缺陷:

var statePromiseMock = {};
beforeEach(module('app.services', {
  $state: {
    go: jasmine.createSpy().and.returnValue(statePromiseMock)
  }
}));

并测试如下:

it('...', inject(function (HttpInterceptorService, $state) {
    var state404Promise = HttpInterceptorService.responseError({ status: 404 });
    expect($state.go).toHaveBeenCalledWith('404');
    expect(state404Promise).toBe(statePromiseMock);
    ...
}))

即。它可能像

describe('HttpInterceptorService', function() {

  // Bindable members
  var HttpInterceptorService;

  var statePromiseMock = {};
  beforeEach(module('app.services', {
    $state: {
      go: jasmine.createSpy().and.returnValue(statePromiseMock)
    }
  }));

  // Bind references to global variables
  beforeEach(inject(function(_HttpInterceptorService_) {
    HttpInterceptorService = _HttpInterceptorService_;
  }));

  // Check service exists with methods
  it('Exists with required methods', function() {
    expect(HttpInterceptorService).toBeDefined();
    expect(angular.isFunction(HttpInterceptorService.response)).toBe(true);
    expect(angular.isFunction(HttpInterceptorService.responseError)).toBe(true);
  });

  it('...', inject(function($state) {
    var state404Promise = HttpInterceptorService.responseError({
      status: 404
    });
    expect($state.go).toHaveBeenCalledWith('404');
    expect(state404Promise).toBe(statePromiseMock);
  }))

});