简体   繁体   English

单元测试Angular HTTP拦截器

[英]Unit testing Angular HTTP interceptor

I have a standard HTTP interceptor as a factory: 我有一个标准的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');
    }
  }

}

In manual testing, I can see this works correctly by redirecting the user to the relevant 404 or error page if an HTTP call returns an error response. 在手动测试中,如果HTTP调用返回错误响应,则可以通过将用户重定向到相关的404或错误页面来看到此方法是否正确。 The basic principal of this is documented by Angular here: https://docs.angularjs.org/api/ng/service/ $http#interceptors Angular在此处记录了其基本原理: https : //docs.angularjs.org/api/ng/service/ $ http#interceptors

Now I'm trying to write a unit test with Karma & Jasmine to test that the responseError function works correctly. 现在,我正在尝试使用Karma&Jasmine编写单元测试,以测试responseError函数是否正常工作。 I've checked out this SO answer to help me. 我已经检查了这个SO答案来帮助我。 My test looks like this: 我的测试看起来像这样:

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');
    });
  });

});

My test passes the Exists with required methods check but fails Sets window location with the following error: 我的测试通过了Exists with required methodsExists with required methods检查,但未通过以下错误Sets window location

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

The module doesn't seem to have ui.router module loaded, hence $state service is undefined. 该模块似乎未加载ui.router模块,因此$state服务未定义。 This is fine, because real router introduces extra moving parts and is highly undesirable in unit tests. 很好,因为真正的路由器会引入额外的运动部件,并且在单元测试中是非常不希望的。

For functional test it is normal to treat a unit as a blackbox, provide initial conditions and test the results, asserting window.location would be appropriate. 对于功能测试,通常将一个单元视为黑盒,提供初始条件并测试结果,断言window.location是合适的。

For unit test there's no need to treat a unit as a blackbox, $state service may be stubbed: 对于单元测试,无需将单元视为黑盒, $state服务可能会存根:

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

And tested like: 并测试如下:

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

Ie it may be something like 即可能是这样的

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);
  }))

});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM