【问题标题】:Making real requests to HTTP server in AngularJS unit/integration tests在 AngularJS 单元/集成测试中向 HTTP 服务器发出真实请求
【发布时间】:2017-02-20 03:25:04
【问题描述】:

在 Angular 1.x 单元/集成测试中发出未使用 $httpBackend.when 模拟的请求会导致错误:

错误:意外请求:GET /real-request

是否可以使用ngMock 和 Karma+Jasmine 测试台发出真正的 HTTP 请求?这样做的好习惯是什么?

【问题讨论】:

  • 请解释一下在您的情况下什么是“真正的”HTTP-Request?
  • 真正的请求是调用window.XMLHttpRequest向HTTP服务器发起请求的请求。 Angular 单元测试中的设计不允许这样做。
  • @lin 这是自我回答,顺便说一句。我认为这是一个臭名昭著的问题。
  • @lin 有一些黑客行为,是的。它可以被认为是单元测试中的 XY 问题(在 SO 上看到了一堆这样的问题),但 IMO 在集成测试中非常好(Angular 团队没有考虑到这一点)。

标签: javascript angularjs unit-testing jasmine integration-testing


【解决方案1】:

AngularJS 是一个自以为是的框架,它对单元测试中的 HTTP 请求的看法是所有这些都应该被模拟。

不建议在单元测试中执行真正的 HTTP 请求,原因有两个。单元测试应该是独立的并且是快速的。发出真正的请求会使测试异步,这会显着减慢测试运行速度。发出真正的请求会打破隔离,测试是否通过取决于测试单元和后端。

在设计 AngularJS ngMock 模块时考虑到了这一点(它在单元测试中由 angular-mocks.js 自动加载)。开发人员几乎不会使用 Angular 进行 asynchronous Jasmine 单元测试,因为没有必要这样做。

集成测试不同。它们可能不像 E2E 测试(通常由 Protractor 运行)那样广泛,并且测试多个单元如何协同工作,这可能包括后端(HTTP 服务器)。所以最终还是使用了 Karma 和 Jasmine,但是测试可能会更慢和异步,并且会做真正的 HTTP 请求。

这是 ngMockE2E 模块(通常用于 E2E 测试)发挥作用的地方。它与 ngMock 一起包含在 angular-mocks.js 中,但默认情况下不加载。

ngMockE2E 是一个 AngularJS 模块,其中包含适用于端到端测试的模拟。目前这个模块中只有一个模拟 - e2e $httpBackend 模拟。

ngMockE2E 包含不同的$httpBackend 实现,可用于此目的。它的 API 各不相同。它不应该使用flushextend 方法。如果有 $q 应执行的承诺链,则可以使用 $rootScope.$digest()

ngMockE2E 在使用其帮助函数 moduleinject 时,由于 ngMock 正在对 Angular 服务进行调整,因此无法开箱即用。可以使用用于集成测试的辅助模块:

angular.module('ngMockI9n', []).config(function ($provide) {
  // hack to restore original implementations which were overridden by ngMock
  angular.injector(['ng', function ($httpBackendProvider, $browserProvider) {
    $provide.provider('$httpBackend', $httpBackendProvider);
    $provide.provider('$browserI9n', $browserProvider);
  }]);

  // make ngMockE2E $httpBackend use original $browser
  var httpBackendI9nDecorator = angular.mock.e2e.$httpBackendDecorator
  .map(function (dep) {
    return (dep === '$browser') ? '$browserI9n' : dep;
  });
  $provide.decorator('$httpBackend', httpBackendI9nDecorator);
});

此外,whitelisted real HTTP requests 的配方可用于简化测试,但最佳做法是显式枚举真实和模拟请求。

beforeEach(module('app'));  
beforeEach(module('ngMockI9n'));

beforeEach(inject(function ($httpBackend) {
  $httpBackend.when('GET', '/mocked-request').respond(200, {});

  // all other requests will be automatically whitelisted and treated as real
  // so make sure that mocked requests are mocked above this point
  angular.forEach(['GET', 'DELETE', 'JSONP', 'HEAD', 'PUT', 'POST', 'PATCH'],
  function (method) {
    $httpBackend.when(method).passThrough();
  });
}));


it('does real async request', function (done) {
  // async tests need extra `done` param
  inject(function () {
    $http.get('real-request').then(function (response) {
      expect(response.data).toEqual(...);
    })
    .then(done, done.fail);

    $rootScope.$digest();
  });
});

it('does mocked sync request', function (done) {
  // tests with mocked requests are async, too
  inject(function () {
    $http.get('mocked-request').then(function (response) {
      expect(response.data).toEqual(...);
    })
    .then(done, done.fail);

    $rootScope.$digest();
  });
});

TL;DR:在实际请求的集成测试中使用来自ngMockE2E$httpBackend,这需要一些额外的工作才能使其与ngMock 兼容。永远不要在单元测试中做真正的请求,这会导致缓慢而无用的测试。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多