在 AngularJS 单元/集成测试中向 HTTP 服务器发出真实请求 [英] Making real requests to HTTP server in AngularJS unit/integration tests

查看:22
本文介绍了在 AngularJS 单元/集成测试中向 HTTP 服务器发出真实请求的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Making a request that wasn't mocked with $httpBackend.when in Angular 1.x unit/integration test results in an error:

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

Error: Unexpected request: GET /real-request

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

Is it possible to make real HTTP requests with ngMock and Karma+Jasmine test rig? What is a good practice to do that?

推荐答案

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

AngularJS is opinionated framework, and its opinion on HTTP requests in unit tests is that all of them should be mocked.

不建议在单元测试中进行真正的 HTTP 请求,原因有二.单元测试应该是隔离的并且是快速的.发出真实请求会使测试异步,这会显着降低测试运行速度.发出真正的请求会打破隔离,测试是否通过取决于被测试单元和后端.

It is not advisable to do real HTTP requests in unit tests for two reasons. Unit tests are supposed to be isolated and be fast. Making a real request makes a test asynchronous, which slows down test runs significantly. Making a real request breaks the isolation, the fact if a test passes depends on both tested unit and a backend.

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

This was taken into consideration when AngularJS ngMock module was designed (it is loaded automatically in unit tests by angular-mocks.js). The developer will hardly ever do asynchronous Jasmine unit tests with Angular, because there's no need to do that.

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

Integration tests differ. They may be not as broad as E2E tests (which are often run by Protractor) and test how several units work together, this may include a backend (HTTP server). So in the end Karma and Jasmine are still used, but the tests may be slower and asynchronous and do real HTTP requests.

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

This is where ngMockE2E module (usually used in E2E tests) kicks in. It is included in angular-mocks.js alongside with ngMock but isn't loaded by default.

ngMockE2E 是一个 AngularJS 模块,其中包含适合端到端测试的模拟.目前该模块中只有一个模拟 - e2e $httpBackend 模拟.

The ngMockE2E is an AngularJS module which contains mocks suitable for end-to-end testing. Currently there is only one mock present in this module - the e2e $httpBackend mock.

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

ngMockE2E contains different $httpBackend implementation which can be used for the purpose. Its API varies. It isn't supposed to use flush and extend methods. $rootScope.$digest() may be used if there are $q promise chains that should be executed.

ngMockE2E 将无法开箱即用,因为 ngMock 在其辅助函数 module<时对 Angular 服务进行了调整/code> 和 inject 被使用.可以使用用于集成测试的辅助模块:

ngMockE2E won't work out of the box properly because of the the adjustments that are being made to Angular services by ngMock when its helper functions module and inject are used. A helper module for intergration tests can be used instead:

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

此外,可以使用列入白名单的真实 HTTP 请求的方法来简化测试,尽管最佳做法是显式枚举真实请求和模拟请求.

Additionally, a recipe for whitelisted real HTTP requests can be used to make the testing easier, although the best practice is to enumerate real and mocked requests explicitly.

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 兼容.切勿在单元测试中执行真正的请求,这会导致测试缓慢且无用.

TL;DR: Use $httpBackend from ngMockE2E in integration tests for real requests, this requires some extra work to make it compatible with ngMock. Never do real requests in unit tests, this results in slow and trashy tests.

这篇关于在 AngularJS 单元/集成测试中向 HTTP 服务器发出真实请求的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆