在AngularJS单元/集成测试中向HTTP服务器发出实际请求

时间:2017-02-19 19:08:36

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

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

  

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

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

1 个答案:

答案 0 :(得分:6)

AngularJS是一个自以为是的框架,它对单元测试中的HTTP请求的意见是所有这些都应该被嘲笑。

出于两个原因,不建议在单元测试中执行真正的HTTP请求。单元测试应该是隔离的并且速度很快。发出实际请求会使测试异步,这会显着降低测试运行速度。提出真正的请求可以打破隔离,如果测试通过则取决于测试单元和后端。

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

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

这是ngMockE2E模块(通常在E2E测试中使用)启动的地方。它包含在angular-mocks.js和ngMock旁边,但默认情况下不会加载。

  

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

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

ngMockE2E无法正常使用,因为ngMock在帮助函数module和{{1}时对Angular服务进行了调整使用。可以使用用于集成测试的辅助模块:

inject

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

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

TL; DR:在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(); }); }); 中使用$httpBackend进行实际请求的集成测试,这需要一些额外的工作才能使其与ngMockE2E兼容。在单元测试中永远不要做真正的请求,这会导致测试缓慢且无用。