在 AngularJS unit/integration 测试中向 HTTP 服务器发出真实请求
Making real requests to HTTP server in AngularJS unit/integration tests
发出未在 Angular 1.x unit/integration 中使用 $httpBackend.when
模拟的请求导致错误:
Error: Unexpected request: GET /real-request
是否可以使用 ngMock
和 Karma+Jasmine 测试平台发出真正的 HTTP 请求?这样做的好习惯是什么?
AngularJS是自以为是的框架,它对单元测试中的HTTP请求的看法是,所有这些都应该被mock。
不建议在单元测试中进行真正的 HTTP 请求,原因有二。单元测试应该是独立的并且是快速的。发出真正的请求会使测试异步,这会显着减慢测试速度 运行s。发出真正的请求会打破隔离,测试是否通过取决于测试单元和后端。
设计AngularJS ngMock
模块时考虑到了这一点(它在单元测试中由angular-mocks.js自动加载)。开发人员几乎不会用 Angular 进行 asynchronous Jasmine 单元测试,因为没有必要这样做。
集成测试不同。它们可能不像 E2E 测试(Protractor 通常 运行)那么广泛,并且测试多个单元如何协同工作,这可能包括后端(HTTP 服务器)。所以最后还是用了 Karma 和 Jasmine,但是测试可能会比较慢,而且是异步的,做真正的 HTTP 请求。
这是 ngMockE2E
模块(通常用于 E2E 测试)的用武之地。它与 ngMock
一起包含在 angular-mocks.js 中,但默认情况下不加载。
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 各不相同。它不应该使用 flush
和 extend
方法。如果有 $q
个应执行的承诺链,则可以使用 $rootScope.$digest()
。
ngMockE2E
开箱即用,因为 ngMock
正在对 Angular 服务进行调整,当它的助手功能 module
和 inject
被使用。可以改用用于集成测试的辅助模块:
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);
});
此外, 的配方可用于简化测试,但最佳做法是显式枚举真实请求和模拟请求。
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
兼容。永远不要在单元测试中做真正的请求,这会导致测试缓慢而无用。
发出未在 Angular 1.x unit/integration 中使用 $httpBackend.when
模拟的请求导致错误:
Error: Unexpected request: GET /real-request
是否可以使用 ngMock
和 Karma+Jasmine 测试平台发出真正的 HTTP 请求?这样做的好习惯是什么?
AngularJS是自以为是的框架,它对单元测试中的HTTP请求的看法是,所有这些都应该被mock。
不建议在单元测试中进行真正的 HTTP 请求,原因有二。单元测试应该是独立的并且是快速的。发出真正的请求会使测试异步,这会显着减慢测试速度 运行s。发出真正的请求会打破隔离,测试是否通过取决于测试单元和后端。
设计AngularJS ngMock
模块时考虑到了这一点(它在单元测试中由angular-mocks.js自动加载)。开发人员几乎不会用 Angular 进行 asynchronous Jasmine 单元测试,因为没有必要这样做。
集成测试不同。它们可能不像 E2E 测试(Protractor 通常 运行)那么广泛,并且测试多个单元如何协同工作,这可能包括后端(HTTP 服务器)。所以最后还是用了 Karma 和 Jasmine,但是测试可能会比较慢,而且是异步的,做真正的 HTTP 请求。
这是 ngMockE2E
模块(通常用于 E2E 测试)的用武之地。它与 ngMock
一起包含在 angular-mocks.js 中,但默认情况下不加载。
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 各不相同。它不应该使用 flush
和 extend
方法。如果有 $q
个应执行的承诺链,则可以使用 $rootScope.$digest()
。
ngMockE2E
开箱即用,因为 ngMock
正在对 Angular 服务进行调整,当它的助手功能 module
和 inject
被使用。可以改用用于集成测试的辅助模块:
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);
});
此外,
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
兼容。永远不要在单元测试中做真正的请求,这会导致测试缓慢而无用。